Plugging memory leaks in your iPhone/iPad app
I’ve been doing a lot of iOS development recently, and just got around to looking at all the memory leaks in my app! Ah, quite the education.
Turns out XCode has some awesome tools for tracking down memory leaks. Today I’m gonna give a short tutorial on using Instruments to find and fix your leaks.
Just fire up XCode with your project, build, then select “Run -> Run with Performance Tool -> Leaks”. This will fire up the Instruments app (a separate app) and automatically create a “Leaks” instrument, and it will start your app in the simulator.
Run your app for a bit, then switch over to the Instruments page and click the “Record” button to stop recording.
Now select the “Leaks” instrument, and you’ll get a display like this:

The main view is the table towards the bottom. I find the “Leaked Blocks” view the most helpful.
That table will list all the leaked objects from your app. Now “leaked” just means that there’s no more references in your app to a particular item on the heap, but the reference count for that item is greater than 0 so the system can’t remove it.
In the example above, we can see very simply that my FeedViewController is leaking an instance of UIBarButtonItem.
Now here’s where it gets interesting. Click that little arrow in the Address column of the table and you get a display like this:

This shows the reference count history for that object. The first row shows that we alloc’d the button in the FeedViewController. The next two rows are down in the system, a “retain” followed by a “release”. Presumably this where the UINavigationBar retained the button while it was showing it.
All reference counts for your objects should start at 1 after alloc, and then eventually get back to 0. When they don’t is when you have a leak. The ref counting is equalized in the system, so we can infer from the history that we’re missing a “release” in FeedViewController. And sure enough, here’s the code:
self.navigationItem.rightBarButtonItem =
[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSearch...];
Classic. Remember “Alloc” always sets the ref count to 1, so you need either a release or an autorelease to decrement it. The easiest fix here is just this:
self.navigationItem.rightBarButtonItem =
[[[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemSearch...] autorelease];
Rebuild, run with Leaks, and sure enough the leak is gone. After working with this tool you start to understand the patterns indicative of typical errors. Can you guess the problem just by looking at this ref count history?

In this case we are alloc’ing the object and assigning it to a “retain” property. You can see the ‘alloc’ and the ‘retain’ from the property. And we can see that we called ‘autorelease’ after the alloc, and indeed the autorelease pool called ‘release’ later. So what’s missing? Hmm…seems like their should be a ‘release’ to go with the ‘retain’ from the property setXXX. And indeed, when I checked out the source, we were simply missing the ‘dealloc’ function altogether. The same thing would happen if you didn’t use ‘self.property = nil’ or ‘[self.property release]‘.
I’ve worked out some good rules-of-thumb when using the Leaks tool:
- Test small sections of code one at a time. This makes things much easier.
- In the Leaked Blocks list, look for the top-most objects first. Often you’ll find that object B was reported leaked, but that happened because object A which contains B was itself leaked. So always start at the top of the object graph.
- Click into the history of a leaked object and carefully match reference count increments to decrements. This will usually show you where the problem is.
The pictures in dropbox for this item are not there anymore:
http://dl.dropbox.com/u/1892875/Screenshots/z1xp.png