在`-dealloc`中调用的异步方法可能会生成不需要的僵尸对象

时间:2014-08-05 21:32:53

标签: ios objective-c automatic-ref-counting grand-central-dispatch

当我走过一些代码时,我几天前偶然发现了这个问题,

- (void)dealloc {
    ...
    [self.postOfficeService deregister:self];
    ...
}

邮局服务的取消注册是异步操作,即使从界面不明显,因为没有传递给postOfficeService的块或函数。< / p>

postOfficeService的-deregister方法的内部实现类似于

// -deregister:(id)formerSubscriber implementation
//some trivial checks here
// deregister former subscriber
dispatch_asynch(_serialQueue, ^{
    [self.subcribers removeObject:formerSubscriber];
});
...

容器self.subscribers完美地完成了它的工作并且仅包含弱引用。即它是NSHashTable

只要在dealloc方法中调用了注销方法,我就会继续崩溃,而postOfficeService试图从其异步块中的列表中删除以前的订阅者,这用于线程安全目的我猜。 在[self.subscribers removeObject:formerSubscriber]上添加断点,可以注意到formerSubscriber对象始终是NSZombieObject。这就是崩溃的原因。

我知道可以在不引发此问题的情况下获得deregister方法的线程安全性 - 我认为应该足够使用dispatch_synch来代替dispatch_asynch版本 我认为这是不应在dealloc方法中调用异步方法的原因之一。

但问题是,即使我们处于ARC环境并且容器对象是NSHashTable(因此它应该正常工作,我猜),如何有可能不断获取NSZombie对象?

4 个答案:

答案 0 :(得分:2)

规则是:当调用dealloc时,一旦dealloc返回其调用者(当引用计数为0时调用release),对象就会消失,并且没有任何东西可以防止这种情况发生。

在ARC之前,你可能试图在dealloc中保留一个对象 - 没有帮助;一旦调用了dealloc,对象就会进入(如果你在dealloc中进行保留/释放,则dealloc只会被调用一次)。 ARC也会自动执行相同操作。

答案 1 :(得分:0)

使用ARC并不意味着你所有的记忆问题神奇地消失了。

发生了什么

  • ARC调用的[obj发布]

    • [obj dealloc]

      • [obj.postOfficeService deregister:obj]
        • [obj retain] - 抱歉,您无法取消释放过程
        • dispatch_async
    • free(obj) - 从这一点来说,obj是一个僵尸

  • GCD计划任务

  • dispatch_async执行任务
    • 使用obj - crash

正确的解决方案是使用dispatch_sync确保在解除分配后不尝试使用对象。 (小心死锁)

答案 2 :(得分:0)

  1. 不要从dealloc调用异步清理方法。这不是一个好主意。您的-deregister应该是同步的。
  2. NSHashTable存储指针 - 它相当于__unsafe_unretained或assign - 除非它是使用+weakObjectsHashTable或等效的选项集(NSHashTableZeroingWeakMemoryNSPointerFunctionsObjectPersonality)创建的。如果它不是以这种方式创建的,那么很可能你会有值指向僵尸对象。
  3. &#34;为什么我会得到僵尸&#34;最好通过使用仪器中的Zombies模板分析您的应用程序并激发所需行为来解答。

答案 3 :(得分:0)

我同意其他人的意见,你应该避免在-dealloc方法中进行异步清理。但是,可以通过将参数设置为-deregister: __unsafe_unretained来解决此问题。那么该方法必须将指针纯粹视为不透明值。它不得取消引用或发送消息。很遗憾,您无法控制NSHashTable的实施,也无法保证这一点。即使可以依赖NSHashTable-removeObject:的接口也会采用隐式强对象指针,因此当从不安全的未保留指针复制指针时,ARC可能会保留指针。

您可以使用C函数API来表示哈希表(例如NSHashRemove()),如NSHashTable类概述中所述。