14

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

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

下面是我用来创建自定义容器视图控制器的代码摘录。viewController 指针是 iVar。我也在使用 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;
}

你们都知道我做错了什么吗?

4

3 回答 3

26

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

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

是删除和添加它的方法是

    [parentViewController addChildViewController:childViewController];
    [parentViewController.view addSubview:childViewController.view];
    [childViewController didMoveToParentViewController:parentViewController];
于 2012-12-12T16:54:54.163 回答
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:must of 的这个块具有对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`
    }];
    

我的原始答案如下:


虽然 Srikanth 是正确的,在之后addChildViewController,你应该跟注didMoveToParentViewController:self,在removeFromParentViewController你应该跟注之前willMoveToParentViewController:nil。但这不是你的问题。实际上,我使用了您的代码的变体(即使没有dealloc),并且子控制器可以正常发布。

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

[如果您想查看原始答案的其余部分以及 OP 代码示例的代码片段和次要细节,请参阅修订历史记录]

于 2012-12-12T17:43:46.893 回答
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];

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

谢谢!

于 2012-12-12T18:45:03.330 回答