为什么在使用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)
- 这是启用僵尸对象的正确行为?
- 我应该如何实现这种types的逻辑来消除这个问题。 我想继续使用启用僵尸对象。
编辑#1:根据凯文的build议,我已经提交给苹果和openradar在http://openradar.appspot.com/10537635 。
编辑#2:澄清一个很好的答案
首先,我是一名经验丰富的iOS开发人员,我完全了解ARC,僵尸对象等。如果我错过了某些东西,当然,我很欣赏任何照明。
其次,对于此特定崩溃的解决方法是,当secondaryViewController
被释放时,将observer作为观察者移除。 我也发现,如果我明确地设置actionsController actionsController = nil
当secondaryViewController
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并重试。 并仔细检查你的代码是否有保留条件。