为什么在使用ARC + NSZombieEnabled时不会释放对象

我将我的应用程序转换为ARC,并注意到在视图控制器被释放时,在我的一个视图控制器中分配的一个对象没有被解除分配。 花了一段时间才弄明白为什么。 我在debugging过程中为我的项目启用了“僵尸对象”,事实certificate这是事实。 考虑下面的应用程序逻辑:

1)用户调用RootViewController中的动作,使SecondaryViewController被创build并通过presentModalViewController:animated呈现presentModalViewController:animated

2) SecondaryViewController包含一个ActionsController ,它是一个NSObject子类。

3) ActionsController在初始化时通过NSNotificationCenter观察一个通知,当它被解除分配时停止观察。

4)用户closuresSecondaryViewController返回到RootViewController

启用僵尸对象closures,上述工作正常,所有的对象被释放。 使用ActionsController上的启用僵尸对象不会解除分配,即使SecondaryViewController被释放。

这导致我的应用程序b / c中的问题NSNotificationCenter继续发送通知给ActionsController和结果处理程序导致应用程序崩溃。

我创build了一个简单的应用程序来说明这个在https://github.com/xjones/XJARCTestApp 。 查看与启用僵尸对象控制台日志开/关来validation这一点。

问题(S)

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

编辑#1:根据凯文的build议,我已经提交给苹果和openradar在http://openradar.appspot.com/10537635 。

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

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

其次,对于此特定崩溃的解决方法是,当secondaryViewController被释放时,将observer作为观察者移除。 我也发现,如果我明确地设置actionsController actionsController = nilsecondaryViewController dealloc'ed它将被dealloc'ed。 这两个都不是很好的解决方法B / C它们实际上要求您使用ARC,但代码,如果您不使用ARC(例如显式dealloc中的零iVars)。 一个特定的解决scheme也无助于确定何时这将成为其他控制器中的问题,因此开发人员可以确定何时/如何解决此问题。

一个好的答案将解释如何确定性地知道,当使用ARC + NSZombieEnabled时,你需要做一些特殊的对象,所以它将解决这个具体的例子,也适用于整个项目作为一个整体不离开其他类似的潜力问题。

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

感谢所有!

原来,我写了一些严重的废话

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

有一些isa-swizzling正在进行,可能是在_objc_rootRelease ,所以任何覆盖dealloc仍然应该被启用僵尸调用。 僵尸不会发生的唯一事情是实际调用object_dispose – 至less不是默认情况下。

有趣的是,如果你做了一些日志logging,你会发现即使启用了ARC,你的dealloc实现也会调用它的超类的实现。

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

显然,将NSZombieEnabled设置为YES导致.cxx_destruct完全不被调用 – 至less在编辑示例项目时发生了这种情况:
僵尸closures导致backtrace和both deallocs,而僵尸上没有回溯,只有一个dealloc。

如果你有兴趣,额外的日志logging包含在示例项目的一个分支 – 只是运行的工作:有两个僵尸开/关的共享scheme。


原始(无意义的)答案:

这不是一个错误,而是一个function。

这与ARC无关。

NSZombieEnabled基本上是用来实现一个实现,反过来,这个对象的types就会_NSZombie一旦你发送任何消息,虚拟类就会炸毁。 这是预期的行为,如果我没有完全错误的话 – logging。

这是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 

您可以在我的博客文章中使用ARC和Zombies进行debugging,find更多有关此解决方法的信息。

原来这是一个iOS的错误。 苹果已经联系我,并表示他们已经解决了这个问题在iOS 6。

要回答第二个问题,您需要从NSNotification中删除观察者 – 这将阻止它调用视图。

通常情况下,你会在dealloc中做这个,但是那个僵尸问题也许不会被调用。 也许你可以把这个逻辑viewDidUnload?

因为你打开了NSZombieEnabled,这个让对象不叫dealloc,把对象放到一个特殊的地方。 你可以closuresNSZombieEnabled并重试。 并仔细检查你的代码是否有保留条件。