Google+ Badge

Wednesday, December 14, 2011

A better understanding of iOS memory management

I got a free eBook called "Objective-C Fundamentals" from Manning. Chapter 9 covers memory management, something we all love. While I have read various things about viewDidLoad viewDidUnload and didReceiveMemoryWarning I had not tied them all together. Since my app can get into low memory conditions when using the camera reading this chapter gave me a better understanding of things. It was time to tackle this beast.

The home screen in the app consists of 6 images the user can press. Below those images are labels and behind all of that are shelves that the images sit on. The labels are pasted onto the shelf front. Pretty basic stuff but the images can be large on the iPad or retina iPhone so we need to be a good neighbor and throw them away during low memory conditions.

First mistake I was making - loading all the images in the init method. I should load things in viewDidLoad. I moved all that code which was easy enough.

Second mistake - thinking viewDidLoad and viewDidUnload are called in pairs. Gee, the names sure lead you to believe that will happen but it does not. Apple added viewDidUnload support in iOS 3.0 but did not add pair calling code. Since this view in my app is not using an XIB because I totally change the layout between portrait and landscape I don't get viewDidUnload support even when I call self.view = nil in didReceiveMemoryWarning. If I set it to null then I will get multiple viewDidLoad calls but never a viewDidUnload call.

Here is what happens:

Initial view appears
   viewDidLoad is called
As I go back and forth between the Home view and other views - no other calls made.
Go to a view off the home view (home view not visible at this point)
Force low memory condition in simulator
   didReceiveMemoryWarning is called
     I set self.view = nil
     I thought viewDidUnload would be called - it is not
   Hit "back" button on second view
   Home view becomes visible
   viewDidLoad is called on Home View

Since I am not using an XIB file I have to do all my memory clean up in the didReceiveMemoryWarning call. It all works fine now and running the memory leak detector shows all is good.

I can tell things are working because the images "fly" into place when the view first appears or you rotate the device. First time up they fly from 0,0 to their spot on the shelf. Toggle back and forth between the Home view and another view and they don't fly as they were already in right spot. Toggle to second view, force low memory condition and return to Home view and they fly back in place again. Of course I put in breakpoints to verify all the calls are being done in order too.

The app is being a good citizen. Things are loading in the correct methods. Memory is being dumped in the correct methods. Now if only the methods were actually called in pairs to avoid all this confusion. Such is life when you don't use Interface Builder and IBOutlets due to your interface being different between portrait and landscape. I run 3 shelves in a 2x3 layout in portrait and 2 shelves in a 3x2 layout in landscape.