how to debug EXC_BAD_ACCESS on iPhone

October 31, 2009

EXC_BAD_ACCESS. Debugging this one is on par with figuring out why the wife says “not tonight, honey.” And they are equally unfortunate situations.

Let’s see what we can do about EXC_BAD_ACCESS.

EXC_BAD_ACCESS happens when a message is sent to an object that has already been released. By the time the error is caught, the call stack is usually gone especially if dealing with multiple threads.

How nice would it be to keep a dummy around after the object is released that could stop execution, tell us what message was sent and show us the call stack… well, there’s a way to do just that.

If you set the NSZombieEnabled environment variable, the Objective C runtime will leave a dummy object behind for every deallocated object. When the zombie object is called, execution stops and you can see the message that was sent to the object and the call stack that tells you where the message came from (it doesn’t tell you where you over released the object, but knowing where the object is called from should get you pretty close to the problem.)

To set this variable, go to the info panel of the executable in xcode, and create a new environment variable in the arguments tab by clicking the plus sign in the lower left corner of the window. Name the variable NSZombieEnabled, type YES for the value and make sure that the checkbox is selected.

set NSZombieEnabled variable

set NSZombieEnabled variable

Go ahead and run your program now (in debug mode, because you need the stack information.) When the over released object is accessed, you get an error message similar to this (xcode debug view):

2009-03-30 02:30:36.172 ninjaJumper[3997:20b] *** -[GameLayer retain]: message sent
to deallocated instance 0x59bf670

This shows the class of the object (GameLayer) and the message sent (retain).

Let’s take a look at the stack now:

call stack

call stack

The methods printed in bold are in your code, the others are in some other API. Here you can see that the object was accessed from [Director touchesBegan:withEvent], where an array was copied (most likely the over released object was in the array.)

This information should get you pretty close to the problem.

Once the problem is fixed, make sure that the NSZombieEnabled variable is disabled. You don’t need to delete it, but make sure that the checkbox is unchecked:

NSZombie disabled

NSZombie disabled

Now about the wife. Good luck there. Try a box of chocolate or load the dishwasher for a couple days.

122 Responses to “how to debug EXC_BAD_ACCESS on iPhone”

  1. You saved my time. Thanks for g8 tip!

  2. You Rock!!! I hate when I get these obscure errors. This helped point in the right the direction of many errors within my code. I can imagine using this again and again.

  3. One more thank you on the pile! I’ve just spent 4 hours not realizing that I didn’t remove an observer when I dealloc’ed the class.

    THANK YOU!!!!! 😉

  4. Oh my god! Thank you so much!

  5. And Bookmark! Thanks to you I found the problem in minutes instead of years!

  6. Really nice!

  7. Thanks for the post.

    It didn’t help me with the issue that led me to this post but it helped with another.

    For those that asked – ‘where’ to enable/add in 3.2 (and maybe 4.0):
    On the left side of your xcode screen: Groups & Files
    Click the arrow next to Exacutables, then click your app name under Exacutables so it is highlighted. Click the big blue/white (i) [icon] top-center of xcode.

    I had a working target-iPad uiviewcontroller to download file(s). Had a [Done] button to close the view (sent [connection cancel] if download was active], using a textview for a status box, couple of labels for current file name etc.

    To make it pretty I added a UIProgressView (progress bar). Without even connecting the outlet – ERR_BAD_ACCESS on [self dismiss…] – but only sometimes.

    When I enabled NSZombieEnabled – never crashed. Worked everytime with no messages in console or debugger – never crashed.

    Uncheck NSZombieEnabled – crash *sometimes*.

    After sleeping on it (sometimes staring at code and fighting with it is just a waste) – I fired up xcode, when to my view controller and moved:
    [super dealloc];
    from the top of – (void)dealloc { to the bottom

    What it was (that crashed random):
    – (void)dealloc {
    [super dealloc];
    [doneBtn release]; //my button to close the view
    [prgBar release]; //my progress bar
    }

    Changed to (and now never crashes sim or device):

    – (void)dealloc {
    [doneBtn release]; //my button to close the view
    [prgBar release]; //my progress bar
    [super dealloc];
    }

    Hope the info helps.

  8. Oh ~ You are Greate Man, Thank you Thank You * 10000

  9. Thanks for this helpful tip! Just a note, I had to restart xcode before it worked for me.

  10. Yep. It was a good one. Helped a lot. Thank you. Seeing that EXC_BAD_ACCESS for the first time sucked.

  11. ok so im getting back
    -[__NSArrayM release]: message sent to deallocated instance 0x5d44440
    but have no idea what array its talking about.
    any ideas?

  12. Ahhh well funny story about my problem… i found out (yes im new to object programming) that when i called a method from another page (i.e. [anotherfile alloc] its in the autorelease pool. and I was just over thinking and looking the problem but after a few shoots with my friends and my thinking ability was knocked down I looked at the programming and was like hmmm u know im not actually alloc that on this page lol…another funny story for the books. thanks for your tips I would have never known where to look without them!

  13. but…. since NSZombieEnabled, my EXC_BAD_ACCESS does not appear???
    maybe I should rest for a while

  14. Dean Wiley, you save my wife.
    http://stackoverflow.com/questions/909856/why-do-i-have-to-call-super-dealloc-last-and-not-first also saves my life.
    Another rule of thumb learned, to call [super dealloc] at the end after releasing others.

  15. Thanks for the tip. It was extremely helpful.

    The following is a very good article on NSZombieEnabled:
    http://www.cocoadev.com/index.pl?NSZombieEnabled

    It is required reading if you are thinking of using NSZombieEnabled.

    One excerpt that helped me was:
    Use in Xcode:

    Double-click an executable in the Executables group of your Xcode project.
    Click the Arguments tab.
    In the “Variables to be set in the environment:” section, make a variable called “NSZombieEnabled” and set its value to “YES”.

    There is other important informatin there too, specifically DO NOT leave it set to yes on a release build.

  16. Dude, thanks so much. After a lot of head scratching I did this and it immediately stopped on the line in dealloc that was causing the issue. Done and done. Will buy an app or two of yours in return for the help. Thanks!

  17. Thanks for the tip! If I knew about this earlier it could have saved me hours of development time… Much appreciated!! :)

  18. […] you try to do anything with it. The easiest and most reliable way to turn on NSZombies is detailed over here. Sure enough, a run with Zombies enabled gives me one of […]

  19. Wow – bodaciously awesome tip. I wish I had thought to search for it before I spent an hour and a half trying to figure out what was getting released myself.

  20. Edit Executables not work for me…
    I can’t figure out EXC_BAD_ACCESS
    But it’s strange ,my app runs normal on iphone 3 but CRASH on touch4/iphone4
    my program is send a pic and GPS location to server
    here is the message I got :”Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.
    &
    Using two-stage rotation animation is not supported when rotating more than one view controller or view controllers not the window delegate”

    on iphone 3 I got “ERROR: FigCreateCGImageFromJPEG returned -12905. Input (null) was 638420 bytes.”(is this normal ?)

    hope some one can help me…Thanks for all reply

  21. @Codza,
    This help ha shelped me on a TOPN of occasions. On this one in particular, I was getting no message – until I read through the comments here.

    @Dean Wiley,
    Moving my [super dealloc] statement to the bottom of the dealloc method solved this for me. Thanks for the tip!!!

  22. You really deserve a 5 star congratulation !
    Thanks for the time you saved me !

Leave a Reply