I have a custom container UIViewController that has six child UIViewControllers, and a set of tabs that the user interacts with to switch between the child view controllers. The problem is when my container view controller is released the child view controllers are not.
I have verified that the child view controllers are not released by adding some debugging code to their dealloc methods, and they are released as long as their view's are not added to the container view controller's view.
Below is an a excerpt of the code I'm using to create my custom container view controller. The viewController pointers are iVars. I am also using ARC so that is why there are no actual release calls.
- (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;
}
Do you all have any ideas what I'm doing wrong?
As I suspected, the problem is not related to the view controller containment code in the question, but rather your adding of the observers (which you discuss in your answer to this question):
And that you tried to remove it with
So, there are two problems:
If you use
addObserverForName:object:queue:
, this is not the correct way to remove this observer. Instead, define a property to keep track of the observer:Then save a reference to that observer when you create it:
And when you want to remove it, use this reference:
That would ensure that the observer would be removed properly.
The failure of the child view controllers to be released while this observer was in place means that this block that you passed to
addObserverForName:object:queue:
must of had a reference toself
. If you tried to remove this observer correctly (as shown above) indealloc
, you would still have a strong reference cycle (formerly known as a retain cycle). This is resolved in a number of ways, but the most robust pattern is to prevent the strong reference cycle in the first place by using theweakSelf
pattern:My original answer is below:
While Srikanth is right that after
addChildViewController
, you should calldidMoveToParentViewController:self
and beforeremoveFromParentViewController
you should callwillMoveToParentViewController:nil
. But that's not your problem. In fact, I used a variation of your code (even without thedealloc
), and the children controllers are released fine.Bottom line, I suspect your problem rests elsewhere, probably a retain cycle somewhere. For example, do you children have strong reference to the parent? Are you using recurring timers? You reference some tabs. You're not using a tab bar controller, are you? It's got to be something like that.
[Refer to revision history if you want to see rest of the original answer with code snippets and minor details on the OP's code sample]
This is not the way to add and remove child view controllers
is the way to remove and to add it is
After hours and hours of trying to figure out what was going on I finally found what was causing my child view controllers from releasing correctly.
Each view controller had the following notification declared in each so that they could respond to various events.
Turns out for some reason that was keeping my view controllers from releasing correctly. I'm guessing that this added the view controller to the NSNotificationCenters observer list, and wasn't being removed when I did the following line.
So to fix my problem I just changed the notification to register like below.
I have no idea why the way I was registering the notification wasn't allowing my view controller to release correctly, but this seemed to have fixed it. If anyone has any insight on why this issue would happen then please let me know.
Thanks!