在 deinit 中的 NotificationCenter.default.removeObserver(self),为什么 deinit 它甚至被调用?

问题描述

从 ios 9 开始,没有必要取消订阅通知中心,因为 ios 会自动处理这个,但在 ios 9 之前,开发人员必须手动调用 NotificationCenter.default.removeObserver(self) 以避免内存泄漏,并且常见的地方是(在很多教程和 Stackoverflow 帖子中都建议这样做)deinit。所以,我的问题是 - 如何使用 deinit通知中注销,因为 deinit 仅在对象释放之前被调用,所以它的引用计数应该是 0,但肯定不是 - 因为我们仍然订阅通知中心。实现这一点的唯一可能方法似乎是使用弱引用,基本上如果通知中心弱引用对象,上述场景是可能的,但在这种情况下,根本不需要取消订阅,因为对象可以轻松解除分配。有人可以澄清一下这是如何工作的。

解决方法

我认为在 iOS9 NotificationCenter 之前将观察者添加为未保留的指针。因此,在 dealloc 中移除观察者的目的不是为了防止保留循环,而是为了防止在可以向已释放对象发送通知时发生崩溃。

从 iOS9 开始,NotificationCenter 开始使用归零弱引用,因此可以跳过移除观察者。您可以在 release notes 中找到更多详细信息:

在 OS X 10.11 和 iOS 9.0 中,NSNotificationCenter 和 NSDistributedNotificationCenter 将不再向可能被解除分配的注册观察者发送通知。如果观察者能够被存储为一个清零弱引用,底层存储将把观察者存储为一个清零弱引用,或者如果对象不能弱存储(即它有一个自定义的保留/释放机制,会阻止运行时从能够弱地存储对象)它将对象存储为非弱置零引用。这意味着观察者不需要在他们的解除分配方法中取消注册。将路由到该观察者的下一个通知将检测归零的引用并自动取消注册观察者。如果一个对象可以被弱引用,则在释放期间将不再向观察者发送通知;在非弱归零引用观察者的情况下,先前在 dealloc 期间接收通知的行为仍然存在。基于块的观察者通过 -[NSNotificationCenter addObserverForName:object:queue:usingBlock] 方法在不再使用时仍然需要取消注册,因为系统仍然持有对这些观察者的强引用。仍然支持过早移除观察者(弱引用或归零引用)。 CFNotificationCenterAddObserver 不符合此行为,因为观察者可能不是对象。