调用了 Deinit 但对象仍在内存中

Posted

技术标签:

【中文标题】调用了 Deinit 但对象仍在内存中【英文标题】:Deinit Called But Object Still in Memory 【发布时间】:2017-01-26 15:36:04 【问题描述】:

我在 Xcode 8 和 Swift 2.3 中看到分配工具的奇怪行为。我有一个对象 (A),在其上调用 deinit,除了 A 引用的对象之一正在被释放(据我所知,不是一个单独的内存问题),但是对象在分配工具中继续被列为“活动”和持久性。此外,当我尝试调试它的保留时,我看到的只是:

请注意,我已确认 deinit 正在由以下人员执行:

在 deinit 方法中添加打印行 向 deinit 方法添加断点 验证 A 引用的其他对象是否已解除分配,并且它们会收到声称在 A.__deallocating_deinit 方法内部发生的释放 (-1) 引用计数

但是,由于某些未知原因,它似乎一直存在。

【问题讨论】:

您确定没有该对象的其他引用吗?根据 ARC,在引用计数为 0 之前,对象不会被释放。所以您可能会在其他地方(在其他 VC 中)保留该视图的引用。 如果 deinit 被调用(并且已经返回),那么这个对象几乎肯定已经消失了,不管它引用的东西是否还在。通常,如果您遇到此类问题,则会出现您没想到的对象的第二个副本。 (“有第二个你没想到的副本”实际上是各种看似不可能的行为的原因。)检查内存地址。但我要寻找的另一件事是您是否不小心将对象保留在deinit 中。这会让你陷入混乱(但仍然可能不会导致这种症状;我敢打赌额外的副本)。 @RobNapier 我确实确认实际上只创建了一个对象。 deinit 正在执行,但仍未释放对象。虽然我仍然不能 100% 理解为什么会出现这个问题,但我在下面添加了答案,最终为我解决了这个问题。 【参考方案1】:

经过几个小时的搜索,我终于(大部分)弄明白了。

在这种情况下,我的 A 类有 6 个属性,其中一个是 B 类的实例。A 类向 B 类注册块回调。B 类从主运行循环外部接收事件,在一个单独的未正确包装在 @autoreleasepool 中的 NSThread。结果,B 类的保留时间比预期的要长,这导致其带有对 A 的回调的块的保留时间比预期的要长。

我说“大部分”的原因是因为 A 类用[unowned self] 注册了所有这些块。由于一个仍然未知的原因,这似乎足以让 deinit 被执行,但不足以真正释放对象。在 @autoreleasepool 中包装另一个线程允许应用释放 B,这足以释放 A。

【讨论】:

我将暂时不选中这个答案,看看其他人是否能够根据这个答案找出真正解决问题的“最佳”解决方案。 感谢您使用最终找到的可能解决方案来跟进您自己的问题,更多人应该这样做!

以上是关于调用了 Deinit 但对象仍在内存中的主要内容,如果未能解决你的问题,请参考以下文章

从内存中删除 UIView 时,快速 deinit 方法不起作用

没有在 UIViewController 上调用 Deinit,但 Dealloc 是

永远不会调用 Deinit 方法 - Swift Playground

析构方法 deinit

未调用 deinit 的解释

当这行代码执行时 deinit() 停止调用