在 Cocoa 中,我是不是需要在释放对象时从接收 KVO 通知中删除它?

Posted

技术标签:

【中文标题】在 Cocoa 中,我是不是需要在释放对象时从接收 KVO 通知中删除它?【英文标题】:In Cocoa do I need to remove an Object from receiving KVO notifications when deallocating it?在 Cocoa 中,我是否需要在释放对象时从接收 KVO 通知中删除它? 【发布时间】:2010-09-06 01:10:36 【问题描述】:

当我注册了一个对象 foo 以接收来自另一个对象 bar 的 KVO 通知(使用 addObserver:...),如果我随后释放 foo 是否需要向 -dealloc 中的 bar 发送 removeObserver:forKeyPath: 消息?

【问题讨论】:

【参考方案1】:

您需要在 -[NSObject dealloc] 运行之前使用 -removeObserver:forKeyPath: 删除观察者,所以是的,在您的类的 -dealloc 方法中执行此操作会起作用。

比这更好的是有一个确定性的点,任何拥有正在执行观察的对象的东西都可以告诉它它已经完成并且(最终)将被释放。这样,您可以在不再需要进行观察的事物时立即停止观察,而不管它何时实际被释放。

记住这一点很重要,因为 Cocoa 中对象的生命周期并不像某些人认为的那样具有确定性。各种 Mac OS X 框架本身发送您的对象 -retain-autorelease,从而延长它们的生命周期,超出您的预期。

此外,当您过渡到 Objective-C 垃圾回收时,您会发现 -finalize 的运行时间与 -dealloc 的运行时间和上下文非常不同。一方面,终结发生在不同的线程上,所以你真的不能安全地将-removeObserver:forKeyPath: 发送到-finalize 方法中的另一个对象。

坚持-dealloc-finalize 中的内存(和其他稀缺资源)管理,并使用单独的-invalidate 方法让所有者告诉对象您已在确定的点上完成了它;做一些事情,比如删除那里的 KVO 观察。代码的意图会更清晰,需要处理的细微错误也会更少。

【讨论】:

我可以根据经验确认,如果您不移除观察者,您将在无休止的崩溃中遭受痛苦。 从 OS X 10.7 Lion 开始,还有 -removeObserver:forKeyPath:context:,它允许您在传递给 -addObserver:forKeyPath:options:context: 的相同上下文中传递。传递独特的上下文可确保您在删除自己的观察时不会删除其他人的观察。 如果它对我想在此处发布的任何人有所帮助,我正在按名称注册通知,然后尝试通过键取消注册,这会导致崩溃,说明该对象未针对该通知注册,这对我来说很困惑。因此,如果您按名称注册,请确保您使用的是 removeObserver:name:object: 来取消注册,而不是 -removeObserver:forKeyPath:context:【参考方案2】:

我从痛苦的经历中获得的一些额外信息:虽然 NSNotificationCenter 在垃圾回收下运行时使用归零弱引用,但 KVO 没有。因此,您可以在使用 GC 时不移除 NSNotificationCenter 观察者(当使用保留/释放时,您仍然需要移除您的观察者),但您仍然必须移除您的 KVO 观察者,正如 Chris 所描述的那样。

【讨论】:

这是否记录在某处?【参考方案3】:

绝对同意 Chris 关于“在 -dealloc 和 -finalize... 中坚持记忆(和其他稀缺资源)管理”的评论。很多时候,我会看到人们试图在他们的 dealloc 函数中使 NSTimer 对象无效。问题是,NSTimer 保留了它的目标。因此,如果 NSTimer 的目标是 self,则永远不会调用 dealloc,从而导致一些潜在的令人讨厌的内存泄漏。

-invalidate 中无效并在您的deallocfinalize. 中进行其他内存清理

【讨论】:

以上是关于在 Cocoa 中,我是不是需要在释放对象时从接收 KVO 通知中删除它?的主要内容,如果未能解决你的问题,请参考以下文章

不再使用时从 UITableViewCell 中删除观察者

在 posix 线程上创建自动释放池

异步自动释放池

Mac Cocoa:无法释放窗口以取回其内存

Cocoa 绑定 - 得到“在键值观察者仍在注册时释放”警告

Cocoa 中的分布式对象