Ios 容器UIViewController未释放它';s子视图控制器

Ios 容器UIViewController未释放它';s子视图控制器,ios,uiviewcontroller,automatic-ref-counting,Ios,Uiviewcontroller,Automatic Ref Counting,我有一个自定义容器UIViewController,它有六个子UIViewController,还有一组选项卡,用户可以与这些选项卡交互以在子视图控制器之间切换。问题是当我的容器视图控制器被释放时,子视图控制器没有被释放 我已经通过向dealloc方法添加一些调试代码验证了子视图控制器没有被释放,并且只要它们的视图没有被添加到容器视图控制器的视图中,它们就会被释放 下面是我用来创建自定义容器视图控制器的代码摘录。viewController指针是IVAR。我也在使用ARC,这就是为什么没有实际的

我有一个自定义容器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;
}

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

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

    [childViewController willMoveToParentViewController:nil];
    [childViewController view] removeFromSuperview];
    [childViewController removeFromParentViewController];
删除和添加的方法是什么

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

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

你试图用它来移除它

[[NSNotificationCenter defaultCenter] removeObserver:self name:kUpdateEventName object:nil];
因此,有两个问题:

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

    @property (nonatomic, weak) id<NSObject> notification;
    
    如果要删除它,请使用以下引用:

    [[NSNotificationCenter defaultCenter] removeObserver:self.notification];
    
    这将确保观察员将被适当撤职

  • 当此观察者就位时,要释放的子视图控制器失败意味着传递给
    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`
    }];
    

  • 我原来的答覆如下:


    虽然Srikanth是正确的,在
    addChildViewController
    之后,您应该调用
    didMoveToParentViewController:self
    ,在
    从ParentViewController移除之前,您应该调用
    willMoveToParentViewController:nil
    。但那不是你的问题。事实上,我使用了您代码的一个变体(即使没有
    dealloc
    ),子控制器也可以很好地发布

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


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

    经过数小时的努力,我终于找到了导致我的子视图控制器无法正确释放的原因

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

    [[NSNotificationCenter defaultCenter] addObserverForName:UPDATE_EVENT object:nil queue:nil usingBlock:^(NSNotification *note) {
        // do stuff when update happen
    }];
    
    结果是由于某种原因,我的视图控制器无法正确释放。我猜这会将视图控制器添加到NSNotificationCenter观察者列表中,并且在我执行以下操作时没有被删除

    [[NSNotificationCenter defaultCenter] removeObserver:self name:UPDATE_EVENT object:nil];
    
    因此,为了解决我的问题,我只是将通知更改为注册,如下所示

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateEvent:) name:UPDATE_EVENT object:nil];
    
    我不知道为什么我注册通知的方式不允许我的视图控制器正确释放,但这似乎已经修复了它。如果有人对为什么会发生这个问题有任何见解,请让我知道


    谢谢

    出于好奇,您引用了控制显示哪个子级的选项卡。你是如何实现的?我问这个问题是因为我看到您将孩子的
    frame
    设置为
    self.view.frame
    ,所以我不知道您会在家长视图的何处显示此选项卡控件。这只是我实际代码正在执行的一个超快速示例,所以没有100%仔细考虑。在我的实际应用程序中,有一些选项卡沿侧面垂直运行,然后在主视图控制器中有一个视图,子视图控制器视图被放入其中,然后它们的框架被设置为childViewController.frame=contentContainerView.bounds;这是有道理的。乍一看,看到一个占据整个屏幕的遏制示例,我不禁怀疑是否需要遏制。不过,听起来你的计划不错。对不起打扰你了谢谢你的信息员。是的,在你提到我发布的原始代码正在释放孩子们之后,我决定尝试将我的孩子视图控制器类切换到一个新的子类,这个子类除了在Dealoc期间注销标题之外什么都不做。就像你做的一样。完成后,子视图控制器开始正确释放。所以我想我在其他地方有问题。再次感谢你的帮助!我认为,附加到观测者的“更新发生时做事情”块正在关闭对“self”的非块安全引用,并泄漏该引用[
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UPDATE_EVENT object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateEvent:) name:UPDATE_EVENT object:nil];