(if this helps you, even to save a little time, please leave a comment)
Recently, I was working on an iPhone application for a client when I ran headlong into a crash in objc_msgSend. It was a hair-pulling bit of frustration for me as it seemed so hard to debug for a long time. Every time I encountered it, this was all I saw on the stack:
I saw several posts online which gave me more than few hints. It seemed very clear that I had over-released something, but all the zombies and other flags didn’t really make it clear which thing was getting over-released or where. I could tell what it was, but nothing much else until I struck on this idea while taking a walk: if the crashes are always related to the release of an auto-release pool, then I need to control the auto-release pool. I can’t wait for this _NSFireDelayedPerform thing to decide to release it. (Steve Maguire’s “Writing Solid Code” has been one of my favorite industry books for a long time.)
So in essence, I started bracketing all my suspect code like this:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; <code to test> [pool release];
Sure enough, my debugger stacks went from looking like the one above to looking like this:
That’s a LOT better. Now I know where to look. Further application of layers of pools narrowed down the target pretty quickly.
In the end, I found that my mistake was a call like this in an initializer:
NSMutableArray *memberArray = [[NSMutableArray alloc] initWithCapacity: 5];
which isn’t wrong. However, I never actually filled the allocated array entries. When it was released later, it caused my crash. Changing the code to to allocate the array later, when I had items to fill it, fixed the problem.
There is one little problem with this technique that I see: you have to be careful how you bracket things with the pool-release calls. If you release a pool, then try to use something that was allocated on that pool, you may be trying to use something you’ve already released. The easiest way I saw to use it was to put them around a call to a routine or a message. When I had to use them inside a block of code, I had to be careful that allocations and releases that used the pool would all be inside the bracketing calls.
I hope this helps someone else find a solution. If it did, please leave a comment.