Container UIViewController不释放它的子视图控制器

时间:2012-12-12 16:40:49

标签: ios uiviewcontroller automatic-ref-counting

我有一个自定义容器UIViewController,它有六个子UIViewControllers,以及一组用户与之交互以在子视图控制器之间切换的选项卡。问题是当我的容器视图控制器被释放时,子视图控制器不是。

我已经验证通过向dealloc方法添加一些调试代码而不释放子视图控制器,只要它们的视图没有添加到容器视图控制器的视图中就会被释放。

下面是我用来创建自定义容器视图控制器的代码的摘录。 viewController指针是iVars。我也在使用ARC,这就是为什么没有实际的释放呼叫。

- (void)init 
{
    if ((self = [super init])) { 
        vc1 = [[UIViewController alloc] init];
        [self addChildViewController:vc1];

        vc2 = [[UIViewController alloc] init];
        [self addChildViewController:vc2];

        vc3 = [[UIViewController alloc] init];
        [self addChildViewController:vc3];

        vc4 = [[UIViewController alloc] init];
        [self addChildViewController:vc4];

        vc5 = [[UIViewController alloc] init];
        [self addChildViewController:vc5];

        vc6 = [[UIViewController alloc] init];
        [self addChildViewController:vc6];
    }
    return self;
}

- (void)dealloc
{
    [vc1 removeFromParentViewController];
    vc1 = nil;

    [vc2 removeFromParentViewController];
    vc2 = nil;

    [vc3 removeFromParentViewController];
    vc3 = nil;

    [vc4 removeFromParentViewController];
    vc4 = nil;

    [vc5 removeFromParentViewController];
    vc5 = nil;

    [vc6 removeFromParentViewController];
    vc6 = nil;
}

- (void)switchFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController
{
    if (fromViewController) {
        [fromViewController.view removeFromSuperview];
    }

    [self.view addSubview:toViewController];
    toViewController.view.frame = self.view.bounds;
}

你们有什么想法我做错了吗?

3 个答案:

答案 0 :(得分:26)

这不是添加和删除子视图控制器的方法

    [childViewController willMoveToParentViewController:nil];
    [childViewController view] removeFromSuperview];
    [childViewController removeFromParentViewController];

是删除和添加它的方法

    [parentViewController addChildViewController:childViewController];
    [parentViewController.view addSubview:childViewController.view];
    [childViewController didMoveToParentViewController:parentViewController];

答案 1 :(得分:12)

正如我所怀疑的那样,问题与问题中的视图控制器包含代码无关,而是您添加的观察者(您在回答此问题时讨论):

[[NSNotificationCenter defaultCenter] addObserverForName:kUpdateEventName object:nil queue:nil usingBlock:^(NSNotification *note) {
    // do stuff when update happen
}];

你试图用

删除它
[[NSNotificationCenter defaultCenter] removeObserver:self name:kUpdateEventName object:nil];

所以,有两个问题:

  1. 如果您使用addObserverForName:object:queue:,则这不是删除此观察者的正确方法。相反,定义一个属性来跟踪观察者:

    @property (nonatomic, weak) id<NSObject> notification;
    

    然后在创建时保存对该观察者的引用:

    self.notification = [[NSNotificationCenter defaultCenter] addObserverForName:kUpdateEventName object:nil queue:nil usingBlock:^(NSNotification *note) {
        // do something
    }];
    

    如果要删除它,请使用以下参考:

    [[NSNotificationCenter defaultCenter] removeObserver:self.notification];
    

    这样可以确保正确删除观察者。

  2. 在此观察者到位时释放子视图控制器失败意味着您传递给addObserverForName:object:queue:的此块必须具有对self的引用。如果您试图在dealloc中正确删除此观察者(如上所示),您仍然会有一个强大的参考周期(以前称为保留周期)。这可以通过多种方式解决,但最强大的模式是通过使用weakSelf模式首先防止强引用周期:

    typeof(self) __weak weakSelf = self;
    
    self.notification = [[NSNotificationCenter defaultCenter] addObserverForName:kFooNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
        // use `weakSelf` in this block; not `self`
    }];
    

  3. 我的原始答案如下:


    虽然Srikanth在addChildViewController之后是正确的,但您应该致电didMoveToParentViewController:self,在removeFromParentViewController之前,您应该致电willMoveToParentViewController:nil。但那不是你的问题。实际上,我使用了代码的变体(即使没有dealloc),并且子控制器被释放了。

    最重要的是,我怀疑你的问题在其他地方存在,可能是某个地方的保留周期。例如,您的孩子是否有强烈的父母参考?你在使用定期计时器吗?您引用一些选项卡。您没有使用标签栏控制器,是吗?它必须是那样的。

    [如果您想查看原始答案的其余部分,请参阅修订历史记录以及OP代码示例的详细信息]

答案 2 :(得分:2)

经过几个小时的努力弄清楚发生了什么事后,我终于找到了导致我的子视图控制器正确释放的原因。

每个视图控制器都声明了以下通知,以便它们可以响应各种事件。

[[NSNotificationCenter defaultCenter] addObserverForName:UPDATE_EVENT object:nil queue:nil usingBlock:^(NSNotification *note) {
    // do stuff when update happen
}];

由于某些原因导致我的视​​图控制器无法正确释放。我猜这会将视图控制器添加到NSNotificationCenters观察者列表中,并且当我执行以下行时没有被删除。

[[NSNotificationCenter defaultCenter] removeObserver:self name:UPDATE_EVENT object:nil];

因此,为了解决我的问题,我只是将通知更改为如下所示。

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateEvent:) name:UPDATE_EVENT object:nil];

我不知道为什么注册通知的方式不允许我的视图控制器正确释放,但这似乎修复了它。如果有人对此问题的原因有任何见解,请告诉我。

谢谢!