16
Mar
2010
Awesome memory debugging tips for iPhone
I ran into a problem with my current iPhone app I am developing. The application will just crash after a controller is being deallocated.
Running with breakpoints enabled (Cmd + Y) I would get the following message in the console:
Program received signal: “EXC_BAD_ACCESS”.
From experience I already know this has to do with memory being accessed that has been freed. In the callstack it seems to be caused whenever I release an NSArray object. Here is a sample of what the deallocator looks like:
-(void)dealloc
{
for(AVAudioPlayer *p in soundPlayers)
{
[p release];
}
// This is where the crash happens
[soundPlayers release];
[super dealloc];
}
Being still very new to the Objective-C and iPhone way of programming, I decided to just brush up again on the proper way of doing memory management. My initial thought was that I were using NSArray incorrectly.
After a bit of googling (I love this verb) around I found this awesome page http://www.cocoadev.com/index.pl?DebuggingAutorelease
It turns out there is this feature in Cocoa called Zombies and to quote cocoadev.com:
NSZombieEnabled is an environment variable which controls whether the Foundation runtime will use zombies. When zombies are enabled, a deallocated object’s class is dynamically changed to be _NSZombie, and by default, the memory region is never marked as free, although this can be controlled separately.
The end result is that, with zombies enabled, messages to deallocated objects will no longer behave strangely or crash in difficult-to-understand ways, but will instead log a message and die in a predictable and debugger-breakpointable way. This is the tool to use when trying to track down over-releases and premature releases.
How cool is that? Sounds just like what I need.
Along with this there is also a tool called malloc_history that will allow you to track down exactly where a piece of memory was allocated and freed in your program. To use this feature you need to enable MallocStackLogging as well. In order to make my logs more verbose I also turned on MallocStackLoggingNoCompact.
To enable NSZombieEnabled and MallocStackLogging you need to defined the environment variables for your executable.
“Get Info” on your executable in the “Groups & Files” window.

Add the environment variables under the “Arguments” tab.

Right so now that I am armed with this memory debugging weaponry I run my app and wait until the point where it used to crash. And lo and behold I get the following message in the console window:
2010-03-16 17:52:35.560 BlurredNameOfApp[6835:207] *** -[CFString release]: message sent to deallocated instance 0×392bdf0
So I just fire up the command line tool malloc_history (usage: malloc_history <pid> <address>). In my case it looked like this “malloc_history 6835 0×392bdf0″.

In the above image I have highlighted the actual object being allocated and where it was allocated that was causing me this problem. It turns out I was calling release to an object that was returned to me with autorelease – doh!
NSString *filePath = [[NSBundle mainBundle] pathForResource:soundFile ofType:@”caf”];
// … Some more code
[filePath release]; // Yep this is certainly not according to the memory rules!
It is so easy to just get in a groove where you call release to everything even though I know what the good memory management rules are. Luckilly Apple has given us awesome tools to help us track down bugs.
Here are a couple of pages worth reading:
Thanks! This helped me track down a bug after a great deal of pulling out hair.
Glad I could be of help.
For me there is nothing worse than memory bugs or concurrency bugs.