Using Instruments to track down memory leaks
I was prototyping using TMDb for film search in One to Watch to solve the problem of Freebase’s API going away. While doing this I noticed that the memory usage of the app was going up significantly every time I opened a film’s details view.
Using the Allocations tool in Instruments, I could see that the memory was increasing in a staircase like manner. Looking at the memory listing shown in Instruments, it was clear that this stepping was caused by decoded PNG images, used to display the cover art. Each time the details view was shown, another 3MB PNG image appeared in Instruments.
The way displaying a film’s detail works is incredibly simple and standard: the details view, SavedItemDetailsView
is pushed onto a UINavigationContoller
object’s stack. It’s popped off the stack when the view is dismissed. Why wasn’t the SavedItemDetailsView
object getting freed when the details view was popped from the stack maintained by the UINavigationController
?
At first I had a classic “blame the compiler” moment: “the UINavigationController
must be retaining my view for too long!”. StackOverflow, bless its socks, had a bunch of people having similar issues and leaping to blame UINavigationController
too. Of course, it turned out to be my fault. As I assume it did for most of those SO posts.
Thankfully a friend pointed me toward solving the issue by pointing out how to see retain/release calls during the running of the program:
- Start a new Allocations session in Instruments.
- Before starting your run:
- Select the Allocations tool in the top segment of the screen.
- Show the right hand side bar (View -> Inspectors -> Show Record Settings).
- Enable “Record reference counts”.
Now, start the application running using the “record” button in the top left. Do the thing which is causing memory to increase, in my case showing and hiding the details view. As described above, at first I just saw a lot of PNG memory usage. However, this PNG image just got allocated via ImageIO so didn’t give much away. So my first port of call was the parent view controller; it just seemed a good place to start. So I needed to see the retain/release process for the view controller:
- Put the name of the class (
SavedItemDetailsView
in my case) into the search box next to the inspector sidebar. - Hit the tiny right-arrow that appears when you mouseover the class name for one of the instances.
- Hit the tiny right-arrow appearing when hovering over the memory address.
You should get a (potentially very long) list of retain/release calls for your class. In my listing was an obvious unbalanced retain call. Once I found this, I easily tracked down to a delegate
that was strong
rather than weak
. Correcting the property declaration quickly fixed my problem.