为什么使用 ARC 时对象未释放? NSZombie启用
我将我的应用程序转换为 ARC,并注意到当视图控制器被释放时,在我的视图控制器之一中分配的对象没有被释放。我花了一段时间才弄清楚原因。我在调试时为我的项目启用了“启用僵尸对象”,这就是原因。考虑以下应用逻辑:
1) 用户调用 RootViewController
中的操作,导致创建 SecondaryViewController
并通过 presentModalViewController:animated
呈现。
2) SecondaryViewController
包含一个 ActionsController
,它是 NSObject
子类。
3) ActionsController
在初始化时通过 NSNotificationCenter
观察通知,并在释放时停止观察。
4) 用户关闭SecondaryViewController
以返回到RootViewController
。
关闭启用僵尸对象后,上述工作正常,所有对象都被释放。在 ActionsController
上启用 Zombie 对象后,即使 SecondaryViewController
被释放,也不会被释放。
这导致我的应用程序出现问题,b/c NSNotificationCenter 继续向 ActionsController 发送通知,并且生成的处理程序导致应用程序崩溃。
我在 https://github.com/xjones/XJARCTestApp 创建了一个简单的应用程序来说明这一点。查看控制台日志并打开/关闭“启用僵尸对象”以验证这一点。
问题
- 这是启用僵尸对象的正确行为吗?
- 我应该如何实现这种逻辑来消除这个问题。我想继续使用启用僵尸对象。
编辑 #1:根据 Kevin 的建议,我已将其提交给 Apple 和 openradar http://openradar.appspot.com/10537635< /a>.
编辑#2:对一个好答案的澄清
首先,我是一名经验丰富的 iOS 开发人员,我完全了解 ARC、僵尸对象等。如果当然,我错过了一些东西,我很欣赏任何启发。
其次,针对此特定崩溃的解决方法确实是在释放 secondaryViewController 时删除作为观察者的 actionsController 。我还发现,如果在释放 secondaryViewController 时显式设置 actionsController = nil ,它将被释放。这两种方法都不是很好的解决方法,因为它们实际上要求您使用 ARC,但编码就好像您没有使用 ARC(例如,在 dealloc 中显式设置 nil iVars)。特定的解决方案也无助于确定其他控制器中何时会出现此问题,因此开发人员可以确定地知道何时/如何解决此问题。
一个好的答案将解释如何确定性地知道在使用 ARC + NSZombieEnabled 时需要对对象做一些特殊的事情,这样它就可以解决这个特定的示例,并且通常也适用于整个项目,而不会留下其他类似的可能性问题。
完全有可能不存在一个好的答案,因为这可能是 XCode 中的一个错误。
谢谢大家!
I converted my app to ARC and noticed that an object alloc'ed in one of my view controllers was not being dealloc'ed when that view controller was dealloc'ed. It took a while to figure out why. I have Enable Zombie Objects on for my project while debugging and this turned out to be the cause. Consider the following app logic:
1) Users invokes action in RootViewController
that causes a SecondaryViewController
to be created and presented via presentModalViewController:animated
.
2) SecondaryViewController
contains an ActionsController
that is an NSObject
subclass.
3) ActionsController
observes a notification via NSNotificationCenter
when it is initialized and stops observing when it is dealloc'ed.
4) User dismisses SecondaryViewController
to return to RootViewController
.
With Enable Zombie Objects turned off, the above works fine, all objects are deallocated. With Enable Zombie Objects on ActionsController
is not deallocated even though SecondaryViewController
is deallocated.
This caused problems in my app b/c NSNotificationCenter
continues to send notifications to ActionsController
and the resulting handlers cause the app to crash.
I created a simple app illustrating this at https://github.com/xjones/XJARCTestApp. Look at the console log with Enable Zombie Objects on/off to verify this.
QUESTION(S)
- Is this correct behavior of Enable Zombie Objects?
- How should I implement this type of logic to eliminate the issue. I would like to continue using Enable Zombie Objects.
EDIT #1: per Kevin's suggestion I've submitted this to Apple and openradar at http://openradar.appspot.com/10537635.
EDIT #2: clarification on a good answer
First, I'm an experienced iOS developer and I fully understand ARC, zombie objects, etc. If I'm missing something, of course, I appreciate any illumination.
Second, it is true that a workaround for this specific crash is to remove actionsController
as an observer when secondaryViewController
is deallocated. I have also found that if I explicitly set actionsController = nil
when secondaryViewController
is dealloc'ed it will be dealloc'ed. Both of these are not great workaround b/c they effectively require you to use ARC but code as if you are not using ARC (e.g. nil iVars explicitly in dealloc). A specific solution also doesn't help identify when this would be an issue in other controllers so developers know deterministically when/how to workaround this issue.
A good answer would explain how to deterministically know that you need to do something special wrt an object when using ARC + NSZombieEnabled so it would solve this specific example and also apply generally to a project as a whole w/o leaving the potential for other similar problems.
It is entirely possible that a good answer doesn't exist as this may be a bug in XCode.
thanks all!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
事实证明,我写了一些严重的废话
如果僵尸像我最初写的那样工作,打开僵尸将直接导致无数误报......
有一些isa-swizzling正在进行,可能在
_objc_rootRelease
,因此任何dealloc
的重写仍应在启用僵尸的情况下调用。对于僵尸来说,唯一不会发生的事情是对object_dispose
的实际调用——至少默认情况下不会。有趣的是,如果您进行一些日志记录,您实际上会看到即使启用了 ARC,您的dealloc 实现也会调用它的超类的实现。
我实际上假设根本看不到这一点:由于 ARC 生成这些时髦的
.cxx_destruct
方法来处理类的任何__strong
ivars,我希望看到 this 方法调用dealloc
— 如果已实现。显然,将
NSZombieEnabled
设置为YES
会导致.cxx_destruct
根本不会被调用 - 至少在我编辑示例项目时发生了这种情况:僵尸关闭导致回溯和两次释放,而僵尸打开则不产生回溯并且仅产生一次释放。
如果您感兴趣,额外的日志记录包含在示例项目的一个分支中 - 只需运行即可工作:有两个共享僵尸开/关方案。
原始(无意义)答案:
这不是一个错误,而是一个功能。
而且和ARC没有任何关系。
NSZombieEnabled
基本上为一个实现调配了dealloc
,而该实现又将该对象的类型调配为_NSZombie
— 一个会爆炸的虚拟类,如下所示当您向其发送任何消息时。这是预期的行为,并且(如果我没有完全弄错的话)已记录在案。Turns out, I've written some serious nonsense
If zombies worked like I originally wrote, turning on zombies would directly lead to innumerable false positives...
There is some isa-swizzling going on, probably in
_objc_rootRelease
, so any override ofdealloc
should still be called with zombies enabled. The only thing that won't happen with zombies is the actual call toobject_dispose
— at least not by default.What's funny is that, if you do a little logging, you will actually see that even with ARC enabled, your implementation of
dealloc
will call through to it's superclass's implementation.I was actually assuming to not see this at all: since ARC generates these funky
.cxx_destruct
methods to dispose of any__strong
ivars of a class, I was expecting to see this method calldealloc
— if it's implemented.Apparently, setting
NSZombieEnabled
toYES
causes.cxx_destruct
to not be called at all — at least that's what happened when I've edited your sample project:zombies off leads to backtrace and both deallocs, while zombies on yields no backtrace and only one dealloc.
If you're interested, the additional logging is contained in a fork of the sample project — works by just running: there are two shared schemes for zombies on/off.
Original (nonsensical) answer:
This is not a bug, but a feature.
And it has nothing to do with ARC.
NSZombieEnabled
basically swizzlesdealloc
for an implementation which, in turn, isa-swizzles that object's type to_NSZombie
— a dummy class that blows up, as soon as you send any message to it. This is expected behavior and — if I'm not entirely mistaken — documented.这是 Apple 在技术问答 QA1758 中承认的错误。
您可以通过将此代码编译到您的应用程序中来解决 iOS 5 和 OS X 10.7 上的问题:
您可以在我的博客文章 启用 ARC 和 Zombies 进行调试。
This is a bug that has been acknowledged by Apple in Technical Q&A QA1758.
You can workaround on iOS 5 and OS X 10.7 by compiling this code into your app:
You will find more information about this workaround in my blog post Debugging with ARC and Zombies enabled.
事实证明这是一个iOS bug。 Apple 已联系我并表示他们已在 iOS 6 中修复了此问题。
Turns out it is an iOS bug. Apple has contacted me and indicated they've fixed this in iOS 6.
要回答第二个问题,您需要从 NSNotification 中删除观察者 - 这将阻止它调用视图。
通常,您会在 dealloc 中执行此操作,但由于僵尸问题,可能不会调用它。也许你可以把这个逻辑放在 viewDidUnload 中?
to answer the second question you would need to remove the observer from NSNotification - that will keep it from calling the view.
Normally, you would do this in the dealloc but with that zombie issue maybe it's not getting called. Maybe you could put that logic in viewDidUnload?
因为你打开了NSZombieEnabled,这让对象不调用dealloc,并且把对象放到一个特殊的地方。您可以关闭 NSZombieEnabled 并重试。并仔细检查您的代码是否具有循环保留条件。
Because you have open NSZombieEnabled, this let the object not call dealloc, and put the object to a special place. you can close NSZombieEnabled and have try again. And double check if your code have circle retain condition.