按名称删除使用闭包语法创建的 NotificationCenter 观察器是否足够?

Is removing a NotificationCenter observer that was created with closure syntax by name adequate?

我有一些使用块/尾随闭包语法创建的通知,如下所示:

NotificationCenter.default.addObserver(forName: .NSManagedObjectContextObjectsDidChange, object: moc, queue: nil) { note in
    // implementation
}

我后来按名称删除了,如下所示:

NotificationCenter.default.removeObserver(self, name: NSNotification.Name.NSManagedObjectContextObjectsDidChange, object: moc)

我的问题

这样够吗?或者我是否绝对需要将 NSObjectProtocol 保存到它自己的 属性 并使用以下语法删除 属性?

NotificationCenter.default.removeObserver(didChangeNotification)

您绝对需要将 return 值存储在 属性 中,稍后再将其删除。

来自https://developer.apple.com/reference/foundation/nsnotificationcenter/1411723-addobserverforname

Return Value

An opaque object to act as the observer.

当您调用任何一个 removeObserver 方法时,第一个参数是要移除的观察者。当您设置一个块来响应通知时,self 而不是 观察者,NSNotificationCenter 在幕后创建自己的观察者对象并且 return送给你。

Note: as of iOS 9, you are no longer required to call removeObserver from dealloc/deinit, as that will happen automatically when the observer goes away. So, if you're only targeting iOS 9, this may all just work, but if you're not retaining the returned observer at all, the notification could be removed before you expect it to be. Better safe than sorry.

补充一下@Dave 的回答,文档似乎并不总是 100% 准确。根据this article by Ole Begemann there is a contradiction in the doc and self-removing magic was not happening as of iOS 11.2 in his test app

所以答案仍然是"Yes, one needs to remove that observer manually"(是的,self不是观察者,addObserver()方法的结果是观察者)。

这里有一个代码示例,说明正确的实现方式:

在 class A(通知接收者或观察者)中添加观察者时声明返回的变量:

private var fetchTripsNotification: NSObjectProtocol?

在您的初始化方法中将您自己添加为观察者:

init() {
    fetchTripsNotification = NotificationCenter.default.addObserver(forName: .needsToFetchTrips, object: nil, queue: nil) { [weak self] _ in
        guard let `self` = self else {
            return
        }
        self.fetchTrips()
    }
}

在您的 class 的 deinit 方法中,确保删除观察者:

deinit { NotificationCenter.default.removeObserver(fetchTripsNotification as Any) }

在您的 class B(通知发布者)中像往常一样触发通知:

NotificationCenter.default.post(name: .needsToFetchTrips, object: nil)