【问题标题】:Understanding ViewController dealloc process了解 ViewController 的 dealloc 过程
【发布时间】:2013-05-14 21:07:59
【问题描述】:

我有一个容器 UIViewController,它在删除它的一个子项时执行以下操作:

- (void)removeChildWithIndex:(NSUInteger)Index {
  @autoreleasepool {
    ChildViewController *child = [_children objectAtIndex:Index];

    //Remove the child from the VC hierarchy
    [child willMoveToParentViewController:nil];
    [child.view removeFromSuperview];
    [child removeFromParentViewController];

    //Remove the child from array
    [_children removeObjectAtIndex:Index];
  }

  //Post a notification for anyone who might care
  [[NSNotificationCenter defaultCenter] postNotificationName:RemovedChildNotification object:self];
}

我的问题的根源是child 不是在@autoreleasepool 块的末尾被dealloced,而是稍后发布(在 RunLoop 有机会之后看起来处理未完成事件的内部列表):

这通常不会成为问题,但是观察在上述函数末尾发出的NSNotification 的一个对象在收到通知之前依赖childdealloced。

谁能解释/链接到一些文档来帮助我理解为什么child 没有立即发布?

或者,如果我别无选择child 何时是dealloced,任何人都可以建议一种干净的方法将我的通知延迟到dealloc 之后?我想我可以拨打[ChildViewController dealloc] 通知父母它的死亡并在那时发出通知,但这是一种非常肮脏的方式......

【问题讨论】:

  • willMoveToParentViewController 不是委托方法吗?你为什么要明确地称呼它?你为什么使用@autoreleasepool;我没有看到任何正在创建的自动释放对象?我认为最好的解决方法是删除“依赖于被释放的孩子”的要求。
  • 观察者真的依赖被释放的孩子吗?而不是依赖它不再有父母?
  • @trojanfoe: This is explained in the docs,但我仍然觉得它非常不直观:添加 VC 时,willMove... 被称为隐式,didMove... 必须被称为显式。删除时情况正好相反。
  • 您对willMoveToParentViewController 的看法是正确的,这对我来说似乎也违反直觉。我喜欢@danypata 建议的在调用通知之前设置child=nil 的想法,但是我仍然觉得真正的解决方案在于围绕观察者中的“依赖于被释放的孩子”要求进行编码。如果不是nil,为什么不在孩子身上设置一个说“我死了”的标志。
  • 当然有道理。不测试dealloced 而是使用私有标志或测试其层次结构而不是将子级与其父级分离的想法怎么样?

标签: ios objective-c cocoa-touch automatic-ref-counting


【解决方案1】:

尝试在下一次运行循环迭代中发送通知:

- (void)removeChildWithIndex:(NSUInteger)Index 
{
    ChildViewController *child = [_children objectAtIndex:Index];

    //Remove the child from the VC hierarchy
    [child willMoveToParentViewController:nil];
    [child.view removeFromSuperview];
    [child removeFromParentViewController];
    [child didMoveToParentViewController:nil];

    //Remove the child from array
    [_children removeObjectAtIndex:Index];

    //Post a notification for anyone who might care
    [self performSelector:@selector(_postRemoveChildNotification) withObject:nil afterDelay:0.0f];
}

- (void)_postRemoveChildNotification 
{
    [[NSNotificationCenter defaultCenter] postNotificationName:RemovedChildNotification object:self];
}

【讨论】:

    【解决方案2】:

    您代码中的主要问题是autoreleasepool whcih

    自动释放池块提供了一种机制,您可以通过它放弃 一个对象的所有权,但避免它被 立即解除分配

    所以基本上autoreleasepool 将帮助您删除自动释放块内对象的所有权,但会保证它们不会立即释放。

    所以你要做的是删除autoreleasepool 块并让你的代码保持原样,在你的代码底部你也可以让孩子为零child = nil 这也将有助于你的班级收到通知。

    另外,不要编写基于在执行代码时某些对象将被释放的事实的代码。 SO 不能保证在调用方法后立即释放一个对象。这只是一个不好的做法,如果可以的话,你应该重新考虑你的实现。

    【讨论】:

    • autoreleasepool 之所以存在是因为a previous answer 让我相信它是必要的。无论哪种方式,child 在触发通知之前都会超出范围。
    • -1 因为删除 autorelease 并将 child 设置为 nil 不会改变任何内容。
    • 好的,所以如果一个答案不能解决你的问题,你投反对票吗?在我看来,如果一个答案是完全错误的,你投反对票,但在这种情况下,你的问题可能在另一个地方。无论如何,您应该发布处理通知的代码,添加视图控制器和自动释放池的代码应该被删除。
    • 是的,如果答案不正确,我会投反对票 - 这不是个人问题,这只是帮助未来用户的最佳方式。另外,我不会删除autoreleasepool - 这只是你反对@robmayoff 的话,我不需要它。我不会发布响应通知的代码,因为它在这里无关紧要 - 改变对dealloc 的依赖并不容易,我宁愿只回答我提出的问题:)
    • 关于autoreleasepool 在苹果文档中明确指出,当您创建大量临时对象(不是您的情况)时应该使用它,并防止对象被立即释放并且您想要恰恰相反。
    猜你喜欢
    • 2013-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多