如何释放包含引用其父视图的视图控制器
这是一个有趣的问题。我有一个视图控制器,我们称之为 MyViewController。它的成员之一是自定义视图,我们称之为 MyCustomView。 MyCustomView 还具有对其父级 MyViewController 的引用,因为 MyViewController 用作自定义视图的委托。当 MyViewController 卸载时会出现问题,例如由于内存警告。发生的情况如下:
首先,为 MyViewController 调用 viewDidUnload。它看起来像这样:
- (void)viewDidUnload {
[super viewDidUnload];
self.myCustomView = nil;
self.someData = nil;
...
}
当执行 self.myCustomView = nil 时,它会触发 myCustomView 被释放。 MyCustomView 的 dealloc 例程看起来像这样:
- (void)dealloc {
...
[delegate release];
...
[super dealloc];
}
回想一下上面的委托是 MyViewController。如果先发布 MyCustomView,这不会成为问题,因为 MyViewController 的引用计数将大于 1,但在这种情况下,MyViewController 已经没有其他对其的引用。这会导致 MyViewController 被释放,即调用它的 dealloc 例程,如下所示:
- (void)dealloc {
[myCustomView release];
[somedata release];
...
[super dealloc];
}
如您所见,MyViewController 的成员(即“somedata”)在 MyViewController 的 viewDidUnload 例程完成之前获得释放。当 MyViewController 和 MyCustomView 的 dealloc 例程完成并且我们回到完成 viewDidUnload 例程时,我们到达了
self.somedata = nil;
现在,somedata 不是 nil 但它的值已经被释放了!这会导致异常。
这似乎是引用计数中的一个严重缺陷。如何处理像这样的对象中导致彼此被释放的双向引用?
一种答案是在 dealloc 例程中始终将成员设置为零。我不喜欢这个答案。这是很多额外的工作,大多数时候是不必要的。另一个答案是重新排列 viewDidUnload 的顺序,以便可能具有向后指针的子对象的释放总是发生在最后。我也不喜欢这个解决方案,有时它甚至可能不起作用。
你如何解决这个问题?
Here is a an interesting problem. I have a view controller, let's call it MyViewController. One of it's members is a a custom view, let's call that MyCustomView. MyCustomView also has a reference to its parent, MyViewController, since MyViewController is used as a delegate for the custom view. The problem occurs when MyViewController is unloaded, say due to a memory warning. Here is what happens:
First, viewDidUnload is called for MyViewController. It looks something like this:
- (void)viewDidUnload {
[super viewDidUnload];
self.myCustomView = nil;
self.someData = nil;
...
}
When self.myCustomView = nil is executed, it triggers myCustomView to be deallocated. MyCustomView's dealloc routine looks something like this:
- (void)dealloc {
...
[delegate release];
...
[super dealloc];
}
Recall from above that the delegate is MyViewController. If MyCustomView were released first, this would not be a problem, as the reference count for MyViewController would be greater than 1, but in this case MyViewController already has no other references to it. This causes MyViewController to be released, namely it's dealloc routine is called, which looks like:
- (void)dealloc {
[myCustomView release];
[somedata release];
...
[super dealloc];
}
As you can see, the members of MyViewController, namely, "somedata" get release BEFORE the viewDidUnload routine for MyViewController ever completes. As the dealloc routines for MyViewController and MyCustomView complete and we get back to finishing the viewDidUnload routine, we get to the line
self.somedata = nil;
Now, somedata is not nil but its value has already been release! This causes an exception.
This seems to be a critical flaw in reference counting. How do you deal with two-way references in objects like this that cause each other to be deallocated?
One answer is to always set the members to be nil in the dealloc routine. I don't like that answer. It's a lot of extra work and shouldn't be necessary most of the time. The other answer is to rearrange the order of viewDidUnload so that deallocation of child objects which may have backwards pointers always happens at the end. I don't like this solution either and it might not even work some of the time.
How do you get around this problem?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
委托成员通常不进行引用计数。通常的声明是:
如果你像这样让你的委托,你就不会有问题。而且它应该是安全的,因为父视图控制器仅与子视图一样长 - 是吗?
delegate members typically aren't reference counted. Typically the declaration is:
If you make your delegate like this you wont have the problem. And it should be safe since the parent view controller is only around as long as the child view - yes?