Break on EXC_BAD_ACCESS in Xcode?

jasonh picture jasonh · Oct 25, 2009 · Viewed 57.3k times · Source

I'm new to iPhone development and Xcode in general and have no idea how to begin troubleshooting an EXC_BAD_ACCESS signal. How can I get Xcode to break at the exact line that is causing the error?


I can't seem to get Xcode to stop on the line causing the problem, but I do see the following lines in my debug console:

Sun Oct 25 15:12:14 jasonsmacbook TestProject[1289] : CGContextSetStrokeColorWithColor: invalid context

Sun Oct 25 15:12:14 jasonsmacbook TestProject[1289] : CGContextSetLineWidth: invalid context

Sun Oct 25 15:12:14 jasonsmacbook TestProject[1289] : CGContextAddPath: invalid context

Sun Oct 25 15:12:14 jasonsmacbook TestProject[1289] : CGContextDrawPath: invalid context

2009-10-25 15:12:14.680 LanderTest[1289:207] *** -[CFArray objectAtIndex:]: message sent to deallocated instance 0x3c4e610

Now, I am attempting to draw to the context I retrieve from UIGraphicsGetCurrentContext() and pass to the object that I want to draw with.


Further trial and error debugging and I found that an NSMutableArray I have a property for on my class was a zombie. I went into the init function for the class and here's the code I was using:

if ((self = [super init])) {
        NSMutableArray *array = [NSMutableArray array];
        self.terrainBlocks = array;
        [array release];
    }
    return self;    
}

I removed the [array release] line and it no longer gives me the EXC_BAD_ACCESS signal, but I'm now confused about why this works. I thought that when I used the property, it automatically retained it for me, and thus I should release it from within init so that I don't have a leak. I'm thoroughly confused about how this works and all the guides and Stackoverflow questions I've read only confuse me more about how to set properties within my init method. There seems to be no consensus as to which way is the best.

Answer

coneybeare picture coneybeare · Oct 25, 2009

For any EXC_BAD_ACCESS errors, you are usually trying to send a message to a released object. The BEST way to track these down is use NSZombieEnabled.

This works by never actually releasing an object, but by wrapping it up as a "zombie" and setting a flag inside it that says it normally would have been released. This way, if you try to access it again, it still know what it was before you made the error, and with this little bit of information, you can usually backtrack to see what the issue was.

It especially helps in background threads when the Debugger sometimes craps out on any useful information.

VERY IMPORTANT TO NOTE however, is that you need to 100% make sure this is only in your debug code and not your distribution code. Because nothing is ever released, your app will leak and leak and leak. To remind me to do this, I put this log in my appdelegate:

if(getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled"))
  NSLog(@"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");

If you need help finding the exact line, Do a Build-and-Debug (CMD-Y) instead of a Build-and-Run (CMD-R). When the app crashes, the debugger will show you exactly which line and in combination with NSZombieEnabled, you should be able to find out exactly why.