EXC_BAD_ACCESS, but with no zombies, and not in debug build configuration

| | August 7, 2015

This is driving me nuts. I just uploaded my first app to the App Store for the first time, and of course, now my app is exploding left and right. When I build with release build configuration, I randomly get an EXC_BAD_ACCESS after banging on the app for some variable amount of time. I turned Zombies back on, but the problem doesn’t appear to be over-releasing a variable, as I don’t get a message about sending a message to a released variable.

The error does always appear in the same spot in my code. It looks like I’m trying to retain a variable that hasn’t been initialized properly. I haven’t the slightest idea how I could be doing that.

But here’s the weird thing: if I build in debug release configuration, IT NEVER CRASHES. I can bang on the thing all day long, and it’s rock solid. I build with Release configuration, and it gets all intermittently crashy.

Looking at the Build settings in the two configurations, there aren’t that many differences. In debug, the GCC 4.0 Optimization level is “None”, while in Release, it’s “Fastest, Smallest.” If I switch the optimization level in Release to “None,” the app behaves itself. Does anyone have a clue what I should be looking for to fix this? Alternately, how many bad things happen if I Distribute with No optimization?

UPDATE:

Dang! I really need a magic debug tool for memory errors. I’ve been working this problem for the last day or so. I added an exercise method that would reliably generate a crash and started looking for the area of code that was causing it.

The app would usually crash the 3rd-5th time I performed one particular type of operation. The only thing that was different about that type of operation was the value I returned from an accessory method. The accessory method generally returned NSDecimalNumbers with values of 0-3, but there was one special case when I was returning an NSDecimalNumber with a non-integer value. I would test the result of the accessory method looking for the non-integer value. I changed the special case to return an NSDecimalNumber with a value of -1 instead of the non-integer value, and I can no longer make the app crash.

Basically, the only change I made was switching from
[[NSNumber numberWithDouble:num] decimalValue] –> crash
to
[[NSNumber numberWithInteger:num] decimalValue] –> no crash

It’s a little but more complicated than that, but not much.

Now, I’m happy that the app no longer crashes but the change I made does not fill me with confidence, as the old bits of code did not appear to be “broken” in any way. So, while my app doesn’t crash anymore, it’s not because I “fixed” it, I just changed some random thing and now it works. 8^(

UPDATE:

Debugger does not spit out a stack trace as it usually does when a program crashes. When it crashes, the debug console outputs the following:

Loading program into debugger…

[… copyright stuff…]

This GDB was configured as "i386-apple-darwin".warning: Unable to read symbols for "/System/Library/Frameworks/UIKit.framework/UIKit" (file not found).

warning: Unable to read symbols from "UIKit" (not yet mapped into memory).

warning: Unable to read symbols for "/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics" (file not found).

warning: Unable to read symbols from "CoreGraphics" (not yet mapped into memory).
Program loaded.

sharedlibrary apply-load-rules all

Attaching to program: `/Users/...', process 10066.

Re-enabling shared library breakpoint 1

Cannot access memory at address 0x4

Cannot access memory at address 0x4

(gdb) 

2 Responses to “EXC_BAD_ACCESS, but with no zombies, and not in debug build configuration”

  1. Adam Rosenfield on November 30, -0001 @ 12:00 AM

    I’d suggest cranking up the warning level on your compiler and see if anything pops up. Open up your project settings and enable every ‘warning’ option you can find. Or, find the project setting named Other C Flags and add the flags -Wall -Wextra.

    You’re going to get a LOT of white noise for things like signed/unsigned mismatches, possible loss of precision, etc., that you can (usually) safely ignore. However, there may be some key warnings that pop up that you definitely should NOT ignore: things like making a pointer from an integer without a cast (or vice-versa). If you’re not 100% sure you can ignore the warning, fix the code so the warning goes away.

  2. If only it were as easy as turning on Zombies and you could always find your over-releases… There are lots of these kinds of errors that Zombies can’t detect. There is no magic debug tool for memory errors; only careful programming (and there are patterns that make these errors much more uncommon, and much easier to debug when they do occur).

    Optimized code can shake free lots of things that don’t show up in unoptimized code. It does suggest slightly that it may be a local variable rather than an ivar, but perhaps not. It may just be timing; being faster may make a race condition go the other way more often.

    If you can get it to crash, take a look of in the stack trace as a first step of course.

    There’s nothing deeply wrong with distributing without optimization, but it’s just masking the problem. There’s a coding error in there. Optimization isn’t breaking your code. Your code is broken.

    There’s a good discussion of debugging memory problems here. The #1 rule is that you must use accessors. They will save you much heartache, so hopefully you already do this. I provide some other pointers in my short discussion of memory management rules.

Leave a Reply