46

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.

Cœur
  • 37,241
  • 25
  • 195
  • 267
jasonh
  • 29,297
  • 11
  • 59
  • 61
  • For what it's worth, a search on that error here on SO (or on Google) doesn't exactly turn up empty... http://stackoverflow.com/search?q=exc_bad_access – Sixten Otto Oct 25 '09 at 21:26
  • 2
    FWIW, I did that search and didn't come up with anything that helped me get XCode to stop on the line that is causing the `EXC_BAD_ACCESS`. Even after turning on NSZombieEnabled and Build and Debug, XCode isn't showing the line that's giving the error. – jasonh Oct 25 '09 at 21:50
  • slightly related: http://stackoverflow.com/questions/1027658/access-nsarray-from-a-nstimer-interval-exc-bad-access basically same error, but thanks to NSTimer. – cregox Jan 12 '11 at 19:02
  • Is that property `retain`ed? If it is, you should `release`, if it isn't, you shouldn`t. – Pål Brattberg May 27 '11 at 17:06
  • 1
    Pål, he shoudl not, because either way, `[NSMutableArray array]`returns an autoreleased instance of `NSMutableArray`, so you need not worry about its memory management. – Ahti Nov 04 '11 at 12:59

10 Answers10

84

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.

Nicholas Riley
  • 43,532
  • 6
  • 101
  • 124
coneybeare
  • 33,113
  • 21
  • 131
  • 183
  • 1
    XCode still isn't stopping on the line that is causing the error, but I have updated the question with the information I see in the debug console. – jasonh Oct 25 '09 at 22:13
  • This is helpful, yet can be misleading because getenv("NSZombieEnabled") will be true (non-zero) regardless of the env variable's actual value. Try this: char* szZombie = getenv("NSZombieEnabled"); if (szZombie && 0 == strcasecmp(szZombie, "YES")) { NSLog(@"NSZombieEnabled enabled!"); } – paiego Aug 11 '11 at 21:33
  • It all depends on how you set the variable. I completely remove it when not using zombie. – coneybeare Aug 12 '11 at 01:18
  • 1
    @coneybeare Link to NSZombieEnabled is dead (404 Not Found). – Pang Nov 11 '15 at 07:30
17

About your array. The line

NSMutableArray *array = [NSMutableArray array];

does not actually give you a retained object but rather an autorelease object. It probably gets retained in the next line but then you should not release it in the third line. See this

This is the fundamental rule:

You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.

epatel
  • 45,805
  • 17
  • 110
  • 144
  • 1
    Thanks. For some reason I thought `[NSMutableArray array]` was like `alloc` and `init`, not a convenience method. I'll get the hang of it sooner or later I'm sure. – jasonh Oct 25 '09 at 23:10
  • 4
    @jasonh, it's both. Class methods like that are, by convention, essentially concatenations of `alloc` and `init`. But the object returned is autoreleased, and so you need to retain the result (essentially, to claim ownership of it from the class where it was allocated). – Sixten Otto Oct 25 '09 at 23:15
  • 3
    So, basically [[NSMutableArray array] retain] is a convenience method to not have to type [[NSMutable alloc] init]? ;) – Henrik Erlandsson Jun 11 '12 at 09:56
10

In Xcode 4, you can enable Zombies by clicking on the Scheme dropdown (top left, right next to the stop button) -> Edit Scheme -> Diagnostics Tab -> Enable Zombie Objects

snez
  • 2,400
  • 23
  • 20
7

Xcode/gdb always breaks on EXC_BAD_ACCESS, you just need to work your way up the call stack to find the code that triggered it.

Note that these kinds of errors often occur with autoreleased objects, meaning that the ultimate cause of the problem won't be in the call stack that triggered EXC_BAD_ACCESS. That's when NSZombieEnabled and NSAutoreleaseFreedObjectCheckEnabled become helpful.

Darren
  • 25,520
  • 5
  • 61
  • 71
5

A new answer to an old thread... in XCode 4 the most effective way to diagnose EXC_BAD_ACCESS exceptions is to use Instruments to profile your app (from XCode click Product/Profile and choose Zombies). This will help you identify messages sent to deallocated objects.

Jonathan Moffatt
  • 13,309
  • 8
  • 51
  • 49
  • 1
    Thanks for your answer, I found Instruments is really helpful. For others with this problem I highly recommend it. It also shows lines of code where the ref count changes along with the stack track. It would have been really hard to find the problem without it! I consider this answer better than the accepted answer, just NSZombieEnabled alone only shows you when the invalid access happens but it doesn't help you find out why. – Hua-Ying Feb 21 '17 at 18:59
2

From the Stanford CS193P classes: if you add a breakpoint (manually, by editing breakpoints) for the symbolobjc_exception_throw you can get a much better picture of what went wrong - letting things proceed to the point where the debugger halts by itself tends to obscure things and screw up the stack trace. When you halt in objc_exception_throw you can often look back to exactly what access/operation caused your problem.

Adam Eberbach
  • 12,309
  • 6
  • 62
  • 114
  • Yes, but he throwing an out of bounds exception right before he segfaults, which is definitely an error and may well cascade into the crash. – Louis Gerbarg Oct 26 '09 at 07:54
2

Another helpful approach is set breakpoints that will trigger directly after the exception occurs:

Open the breakpoints window (Run – Show – Breakpoints) and add two symbolic breakpoints called “objc_exception_throw” and “[NSException raise]"

From: http://blog.emmerinc.be/index.php/2009/03/19/break-on-exception-in-xcode/

Frank
  • 1,426
  • 1
  • 14
  • 14
1

I hope I didn't miss an identical answer, but I've found that it's possible for some projects to throw this error due to running on simulators for older iOS versions that may be incompatible with a project dependency or framework. I was chasing one of these down for too long before realizing it was just happening in the -older- simulator versions, like iPhone 4S, the app wasn't even supposed to try to support.

Would have been nice to have gotten a more verbose error message, but I would imagine that's the responsibility of the framework where it originated... At any rate, this is a fairly common search landing and maybe this will help someone goofing up as badly as I found myself.

1

Just wanted to add for the others who are coming from a web, searching for solutions for the same error but with different mistake. In my case I got the same error when I tried to instantiate NSDictionary with typo in key name where I forgot to add "@" in front of my key:

NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys: myObj1, @"goodKey", myObj2, "badkey @ is missing in front", nil];
Centurion
  • 14,106
  • 31
  • 105
  • 197
0

Before enabling zombies I recommend first get rid of all warnings (if you have any). Simple things like a non void function without a returncan cause this error. If you don't have warnings proceed as the other answers suggest.

user2387149
  • 1,219
  • 16
  • 28