My iOS app is navigation-based with the following structure:
@interface ViewControllerA : UIViewController
@property (strong,nonatomic) ViewControllerB *viewControllerB;
@property (strong,nonatomic) ViewControllerC *viewControllerC;
...
viewControllerB
and viewControllerC
get instantiated before ViewControllerA
's navigationController
pushes them.
In my understanding, everything that is retained in ViewControllerA
should be set to nil in ViewControllerA
's viewDidUnload
.
Should I do the same to child view controllers? Like this:
-(void)viewDidUnload
{
self.viewControllerB=nil;
self.viewControllerC=nil;
}
I found an issue when there was "received memory warning" initiated from viewControllerC
.
Afterward, viewDidUnload
of the parent view controller (i.e. viewControllerA
) was called, thereby setting nil to 'viewControllerB'. Unexpectedly, viewDidUnload of viewControllerB is also called. So I got "message sent to deallocated object" if I set nil to viewControllerB
's subviews (in viewControllerB
's viewDidUnload
).
Does it mean that I should not set nil to child view controllers? What is the best practice for memory management in this situation?
P.S. I use ARC.
After a call to
viewDidUnload
, aUIViewController
should maintain its state, i.e. not releasing anything that cannot be easily recreated. Usually you set to nil any data that is related to the view hierarchy, for example strong references to some subviews or custom data created in theviewDidLoad
. In you example, your parent controller A is expected to be able to recover afterviewDidUnload
was called, meaning that in the future a callviewDidLoad
will restore your controller and not crash.Moreover, all view controllers are registered for memory warning notifications, so when a memory warning occurs they can unload any views that isn't currently shown. I don't know if the order the view controllers are called is deterministic, for example called starting from the top view controller, so you should be cautious when setting a nested
UIViewController
to nil in the parent controllerviewDidUnload
.But as you must maintain the state of yours controllers after
viewDidUnload
, you must not set your child controllers to nil. What you must do is relinquish every strong references you have on the controller subviews (for example a strong reference to a UILabel etc.), but not relinquish the controller itself.