使用ARC + NSZombieEnabled时,为什么对象没有被释放

XJo*_*nes 35 iphone ios nszombieenabled automatic-ref-counting

我将我的应用程序转换为ARC,并注意到在我的一个视图控制器中分配的对象在该视图控制器被解除分配时没有被解除分配.花了一段时间才弄清楚原因.我在调试时为我的项目启用了Zombie Objects,结果证明这是原因.考虑以下应用程序逻辑:

1)用户调用动作RootViewController,导致a SecondaryViewController通过创建和呈现presentModalViewController:animated.

2)SecondaryViewController包含ActionsController一个NSObject子类.

3)ActionsController通过NSNotificationCenter初始化时观察通知,并在取消分配时停止观察.

4)用户解雇SecondaryViewController返回RootViewController.

关闭"启用僵尸对象",上面的工作正常,所有对象都被取消分配.启用Zombie Objects on ActionsController即使SecondaryViewController已取消分配,也不会取消分配.

这导致我的应用程序中的问题b/c NSNotificationCenter继续向其发送通知,ActionsController并且生成的处理程序导致应用程序崩溃.

我创建了一个简单的应用程序,在https://github.com/xjones/XJARCTestApp上说明了这一点.查看控制台日志,启用/启用"启用僵尸对象"以验证这一点.

问题(S)

  1. 这是启用僵尸对象的正确行为吗?
  2. 我应该如何实现这种类型的逻辑来消除这个问题.我想继续使用Enable Zombie Objects.

编辑#1:根据凯文的建议,我已经将其提交给Apple和openradar,网址为http://openradar.appspot.com/10537635.

编辑#2:澄清一个好的答案

首先,我是一位经验丰富的iOS开发人员,我完全理解ARC,僵尸对象等.如果我遗漏了某些东西,当然,我很欣赏任何照明.

其次,这种特定崩溃的解决方法是actionsControllersecondaryViewController解除分配时作为观察者删除.我还发现如果我明确设置actionsController = nil什么时候secondaryViewControllerdealloc'ed它将被dealloc'ed.这两个都不是很好的解决方法b/c它们实际上要求你使用ARC但是代码好像你没有使用ARC(例如在dealloc中明确地使用nil iVars).特定的解决方案也无法确定何时这会成为其他控制器中的问题,因此开发人员确定地知道何时/如何解决此问题.

一个好的答案将解释如何确定性地知道你需要在使用ARC + NSZombieEnabled时对某个对象做一些特殊的事情,这样它就可以解决这个具体的例子并且通常也适用于整个项目而不会留下其他类似的潜力问题.

完全有可能没有好的答案,因为这可能是XCode中的一个错误.

谢谢大家!

dan*_*dee 9

事实证明,我写了一些严肃的废话

如果僵尸像我最初写的那样工作,打开僵尸会直接导致无数的误报......

可能存在一些混乱_objc_rootRelease,所以任何覆盖都dealloc应该在启用僵尸的情况下调用.僵尸不会发生的唯一事情是实际调用object_dispose- 至少不是默认情况下.

有趣的是,如果你进行一点日志记录,你会发现即使启用了ARC,你的实现dealloc也会调用它的超类的实现.

我实际上假设根本没有看到这个:因为ARC生成这些时髦的.cxx_destruct方法来处理类的任何__strongivars,我期待看到这个方法调用dealloc- 如果它已经实现.

显然,设置NSZombieEnabledYES原因.cxx_destruct不被在全称为-至少在那个时候我已经编辑您的样本项目所发生的事情:
僵尸关闭导致回溯和两个deallocs,而对产量没有僵尸回溯,只有一个的dealloc.

如果您感兴趣,额外的日志记录包含在示例项目的一个分支中 - 只需运行:僵尸开/关有两个共享方案.


原创(荒谬)回答:

这不是一个错误,而是一个功能.

它与ARC无关.

NSZombieEnableddealloc对于一个实现来说,实际上是一个混合体,反过来,它是一个对象类型的混合体_NSZombie- 一个虚拟类,只要你向它发送任何消息就会爆炸.这是预期的行为 - 如果我没有完全弄错 - 记录在案.


0xc*_*ced 7

这是Apple在技术问答QA1758中承认的错误.

您可以通过将此代码编译到您的应用中来解决iOS 5和OS X 10.7:

#import <objc/runtime.h>

@implementation NSObject (ARCZombie)

+ (void) load
{
    const char *NSZombieEnabled = getenv("NSZombieEnabled");
    if (NSZombieEnabled && tolower(NSZombieEnabled[0]) == 'y')
    {
        Method dealloc = class_getInstanceMethod(self, @selector(dealloc));
        Method arczombie_dealloc = class_getInstanceMethod(self, @selector(arczombie_dealloc));
        method_exchangeImplementations(dealloc, arczombie_dealloc);
    }
}

- (void) arczombie_dealloc
{
    Class aliveClass = object_getClass(self);
    [self arczombie_dealloc];
    Class zombieClass = object_getClass(self);

    object_setClass(self, aliveClass);
    objc_destructInstance(self);
    object_setClass(self, zombieClass);
}

@end
Run Code Online (Sandbox Code Playgroud)

您可以在我的博客文章" 调试ARC和Zombies"中找到有关此变通方法的更多信息.


XJo*_*nes 4

事实证明这是一个 iOS 错误。Apple 已联系我并表示他们已在 iOS 6 中修复了此问题。