容器UIViewController不释放它的子视图控制器
我有一个自定义的容器UIViewController有六个子UIViewControllers和一组用户交互切换子视图控制器的选项卡。 问题是当我的容器视图控制器被释放子视图控制器不是。
我已经validation了子视图控制器不是通过向它们的dealloc方法添加一些debugging代码来释放的,只要它们的视图没有被添加到容器视图控制器的视图中,它们就被释放。
下面是我用来创build我的自定义容器视图控制器的代码摘录。 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; }
你们有什么想法我做错了吗?
正如我怀疑的那样,问题与视图控制器遏制代码在问题中没有关系,而是你添加观察者(你在这个问题的答案中讨论):
[[NSNotificationCenter defaultCenter] addObserverForName:kUpdateEventName object:nil queue:nil usingBlock:^(NSNotification *note) { // do stuff when update happen }];
而你试图删除它
[[NSNotificationCenter defaultCenter] removeObserver:self name:kUpdateEventName object:nil];
所以,有两个问题:
-
如果使用
addObserverForName:object:queue:
这不是删除此观察者的正确方法。 相反,定义一个属性来跟踪观察者:@property (nonatomic, weak) id<NSObject> notification;
然后在创build时保存对该观察者的引用:
self.notification = [[NSNotificationCenter defaultCenter] addObserverForName:kUpdateEventName object:nil queue:nil usingBlock:^(NSNotification *note) { // do something }];
而当你想删除它,使用这个参考:
[[NSNotificationCenter defaultCenter] removeObserver:self.notification];
这将确保观察员将被正确移除。
-
当这个观察者就位时,子视图控制器的失败意味着你传递给
addObserverForName:object:queue:
must的这个块有一个对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代码片段的代码片段和细节的原始答案的剩余部分,请参阅修订历史logging]
这不是添加和删除子视图控制器的方法
[childViewController willMoveToParentViewController:nil]; [childViewController view] removeFromSuperview]; [childViewController removeFromParentViewController];
是删除和添加它的方式是
[parentViewController addChildViewController:childViewController]; [parentViewController.view addSubview:childViewController.view]; [childViewController didMoveToParentViewController:parentViewController];
经过几个小时,试图找出发生了什么,我终于发现是什么导致我的孩子视图控制器正确释放。
每个视图控制器都在每个视图控制器中声明了以下通知,以便它们可以响应各种事件。
[[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];
我不知道为什么我注册通知的方式不允许我的视图控制器正确释放,但这似乎已经修复了它。 如果任何人有任何见解,为什么会发生这个问题,请让我知道。
谢谢!