我有一个自定义容器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;
}
你们有什么想法我做错了吗?
答案 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];
所以,有两个问题:
如果您使用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];
这样可以确保正确删除观察者。
在此观察者到位时释放子视图控制器失败意味着您传递给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
,在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];
我不知道为什么注册通知的方式不允许我的视图控制器正确释放,但这似乎修复了它。如果有人对此问题的原因有任何见解,请告诉我。
谢谢!