2011-05-26

Understanding EXC_BAD_ACCESS, EXC_BAD_INSTRUCTION/SIGABRT, and zombies

Being primarily a Java developer I was spoilt by the garbage collector of the JVM and therefore I had to face memory management issues when I started learning Objective-C for the iPhone, because that runtime doesn't have a garbage collector.

When an app crashes in most cases access to already released objects is the cause and there are two symptoms which refer to this fact, so let's see who they are and when they occur based on an example related to RestKit.

EXC_BAD_ACCESS
// Here creation and initialization of the params dictionary, etc.
...
SearchDelegate *delegate = [[SearchDelegate alloc] init];
[delegate release];
[[RKObjectManager sharedManager]
    loadObjectsAtResourcePath:@"/search/"
    queryParams:params objectClass:[SearchResult class]
    delegate:delegate];
What's happening here is:
  1. Search request parameters will be prepared
  2. A delegate will be allocated and initialized
  3. The delegate will be released
  4. Some network operation will be done
  5. Last the delegate will be called
The last step fails with EXC_BAD_ACCESS, because the desired object has been released, its assigned memory freed, but not allocated for another object.

EXC_BAD_INSTRUCTION/SIGABRT
SearchDelegate *delegate = [[SearchDelegate alloc] init];
[delegate release];
// Here creation and initialization of the params dictionary, etc.
...
[[RKObjectManager sharedManager]
    loadObjectsAtResourcePath:@"/search/"
    queryParams:params objectClass:[SearchResult class]
    delegate:delegate];
What's happening here is:
  1. A delegate will be allocated and initialized
  2. The delegate will be released
  3. Search request parameters will be prepared
  4. Some network operation will be done
  5. Last the delegate will be called

The last step fails with EXC_BAD_INSTRUCTION/SIGABRT, because the desired object has been released, its assigned memory freed, and also allocated for another object - this is why an exception with a message similar to the following will be thrown:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType objectLoader:didLoadObjects:]: unrecognized selector sent to instance 0x5967460'
We definitely didn't have to do anything with objects of type __NSCFType (it could be __NSCFSet, __NSArrayM, etc), so this is a precise indicator of an object stored on an unwanted memory area.

Ways leading to these errors
  • Over-releasing an object
  • Releasing an object not being an owner of
  • Not retaining an autoreleased object which will be released in the meantime via the autorelease pool
  • Prematurely releasing an object
  • Directly assigning an object to an instance variable instead of the accessor

Finding guilty objects
The exceptions and the subsequent crashes can't be tracked back, because the freed i.e. non-existing object has no information available. The easiest method is to enable the NSZombieEnabled environment variable in the Run phase of the product scheme accessible via the Product | Edit Scheme… menu entry in Xcode:


As a result deallocated memory areas will be allocated by special objects which will be observed by the runtime - these are the so called zombies. Sending a message to a zombie object results in a message similar to the following in the Xcode Console:
-[SearchDelegate respondsToSelector:]: message sent to deallocated instance 0x59be5a0
Furthermore, we can see the following in the Variables View in Xcode:
_delegate = (_NSZombie_SearchDelegate *)0x59be5a0 
Now, this is enough information to solve the issue.

As an alternative the Zombies instrument of Instruments can be used to track down the failure:

7 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Nice Blog, please also let your readers know how to enter the "edit scheme" mode in order to start making the environment variable changes :)

    ReplyDelete
  3. Tiklup, thanks, I've just added the information :)

    ReplyDelete
  4. Thanks a lot! This works for me. I enabled the Zombies and detect the problem.

    []´s from Brazil.

    ReplyDelete
  5. Hey Nice post... been looking for this information for a while now... I have a few doubts about zombies though.

    Zombies will show only when the device is connected to xcode right? For that I need to able to simulate the crash. Which is something I am unable to do. My Code is quite extensive and the app is crashing in production. Looking for deallocated objects in the code manually is like looking for a needle in the haystack.. Any Suggestions???

    ReplyDelete
    Replies
    1. Thanks :)
      Yes, Zombies can only be identified when using a device. Since your app is already in production you have access to the crash call stacks, which you can symbolicate, thus allowing for a reproduction of the steps.
      I suggest that you:
      - extend your test coverage
      - convert your project to ARC.

      Delete