views:

178

answers:

5

I've gotten stung by a bug that I just can't figure out how to debug. Basically I run my code on the simulator and everything is just fine.

When I go to the actual device however, I get a EXC_BAD_ACCESS error. Unfortunately, when running on the phone under the debugger the damned thing works just fine, so I have no way to judge where the error is occurring.

I did get one stack trace that I couldn't reproduce, so I'm pretty sure that the line in my code that is causing the problem is this one (but I can't for the life of me figure out how it could be):

[[NSNotificationCenter defaultCenter] postNotificationName:@"SubscriberChanged" object: nil];

The actual error was on an objc_msgSend about four frames below this line, but its in code that appears to be part of the iPhone SDK, so I don't have the source to inspect it.

Can anyone give me a few pointers on how I might go about figuring out where this problem is? I have a deadline to ship this thing and I can't let it go out like this...

Thanks

--Steve

I finally managed to reproduce this in the debugger. The stack trace I get is as follows:


#0  0x30011940 in objc_msgSend ()
#1  0x3054dc80 in _nsnote_callback ()
#2  0x3024ea58 in _CFXNotificationPostNotification ()
#3  0x3054b85a in -[NSNotificationCenter postNotificationName:object:userInfo:] ()
#4  0x3054dbc0 in -[NSNotificationCenter postNotificationName:object:] ()
#5  0x000027c6 in -[My2CentsAppDelegate handleMOCChange:] (self=0x1159d0, _cmd=0x2bf90, notification=0x147400) at /Users/sdussin/Desktop/UPOD Research LLC/Development/My2Cents/Classes/My2CentsAppDelegate.m:52
#6  0x3054dc80 in _nsnote_callback ()
#7  0x3024ea58 in _CFXNotificationPostNotification ()
#8  0x3054b85a in -[NSNotificationCenter postNotificationName:object:userInfo:] ()

Frame #5 in the stack trace corresponds to the line above.

+2  A: 

If you can't repeat your crash in the debugger, you might try other approaches to tackle it:

  • Review the code (perhaps ask a friend or coworker for a code review)
  • Add logging and assertions
  • Activate all (or at least most) compiler warnings and obey them
  • Make sure to use the [] clang2 static analyzer and obey its warnings

Especially clang is quite good at finding memory management bugs. And EXC_BAD_ACCESS smells like one.

Probably one of the observers that you registered with NSNotificationCenter was released. Remember,

Important: The notification center does not retain its observers, therefore, you must ensure that you unregister observers (using removeObserver: or removeObserver:name:object:) before they are deallocated. (If you don't, you will generate a runtime error if the center sends a message to a freed object.)

oefe
Turns out the notification was the problem. I apparently had several instance of a view object in my NIB. When they got loaded they each registered for the notification, then only one of them got retained. The others were released but failed to de-register for the notification.Thanks all...
Steve
A: 

Usually, when you get a case where something runs on simulator but not the device or vice versa, the cause is a library/framework that is not compiled for the processor it is crashing on.

In your case, you would have an ARM library/framework that works fine on the device but crashes on the simulator running on i386. I would check in the inheritance of whatever object is registered for that notification.

TechZen
+2  A: 

You can use NSZombieEnabled (Google it) to track this sort of thing. Other than that, I second oefe: You probably need to unregister some observer, e.g. a view controller that has been unloaded but is still registered as an observer.

Frank Schmitt
Very good advice - make sure to putif(getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled")) NSLog(@"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!");in your AppDelegate.m to warn you if you forget to turn it off when not required.
Adam Eberbach
sorry about formatting, no code tags in comment box.
Adam Eberbach
One question I had about this: If I'm running on the phone and not using the debugger, how do I see the output of the NSZombieEnabled? I was under the impression that zombies were logged to the console when running under the debugger, but where do they go on the phone?
Steve
My understanding is (but I haven't done it) that a special type of exception is thrown when a zombie is accessed, allowing you to locate the source of the problem.
Frank Schmitt
+1  A: 

The crash is happening because some object was registered for the notification you are sending, and did not un-subscribe before it was released. So when you send the notification it tries to call back that object, and boom.

Look at all the places in your code where you subscribe and see where you missed an unsubscribe (for instance, did you un-subscribe in dealloc?).

Also as mentioned us NSZombieEnabled=YES, an environment flag you set by right-clicking on the executable in the XCode project browser, and going to the Arguments tab and adding it to the environment variables. Then watch the log, when you send the notification you'l see a message saying something like "message blah sent to deallocated instance".

Kendall Helmstetter Gelner
+1  A: 

The tack trace is telling you that an object registered for "SubscriberChanged" notifications has been deallocated. The easiest way to deal with this problem is to look for all classes that register for "SubscriberChanged" notification and have them de-register in their [dealloc] method.

DenNukem