iOS 6 and viewDidUnload

Now that the NDA is no longer in effect, let's talk iOS 6!  IMO, one of the more controversial changes in the new iOS release is the deprecation of the viewDidUnload API in UIViewController. The main reasoning behind this removal is that, according to Apple, their tests showed that tearing down the UI in low memory situations did not actually improve memory pressure in any significant way. I agree and disagree. There are many instances where view controllers are not holding onto a lot of memory and so, yes, you wouldn't see much reduction in memory pressure. I imagine that the heavy hitters will likely be the model data that your controllers are holding onto. But this isn't always the case.

I work on applications with very deep navigation stacks. Think about this use case: car finding and purchasing. There are bound to be many screens in the acquisition process until you reach your goal, any of which may force your app into a low memory situation. You can't completely jettison the model data you have and there may be some instances where your views are holding onto expensive objects like images and web views. These objects are not useful to keep around in a low memory situation. viewDidUnload gave developers an opportunity to purge this memory space at a time where it is safe to do so, but it also provided an easy mechanism to reconstruct the UI when viewDidLoad gets called.

In our aforementioned example, we wouldn't have those hooks and have to do a bit of code gymnastics to tear down the UI and reconstitute it. I feel that viewDidUnload was a simple and straightforward way of managing those situations where maybe you do want to really tear down that UI. I have developed a work around that allows you to keep your viewDidLoad code in place (especially if you support an app pre-iOS 6) and be able to code defensively against low memory situations. I am no necromancer, but we're bringing viewDidUnload back to life:

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // only want to do this on iOS 6
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 6.0) {
        //  Don't want to rehydrate the view if it's already unloaded
        BOOL isLoaded = [self isViewLoaded];

        //  We check the window property to make sure that the view is not visible
        if (isLoaded && self.view.window == nil) {
            //  Give a chance to implementors to get model data from their views
            [self performSelectorOnMainThread:@selector(viewWillUnload)
                                   withObject:nil
                                waitUntilDone:YES];

            //  Detach it from its parent (in cases of view controller containment)
            [self.view removeFromSuperview];
            self.view = nil;    //  Clear out the view.  Goodbye!

            //  The view is now unloaded...now call viewDidUnload             
            [self performSelectorOnMainThread:@selector(viewDidUnload)
                                   withObject:nil
                                waitUntilDone:YES];
        }
    }
}

This would be a pain to have to implement in every view controller you have; my recommendation would be to put this in a base view controller and have everything handled in there for your subclasses.

Posted on Sep 19
Written by Wayne Hartman