6

A friend of mine discovered some strange behaviour with NSDictionary, and I'm curious as to why it happens. Consider the following code:

NSDictionary *dict = [[NSDictionary alloc] init];

// Oops, we can't mutate an NSDictionary
[dict setObject:[[NSNull alloc] init] forKey:@"test"];
NSLog(@"Set");

The code produces a warning upon compilation that "'NSDictionary' may not respond to 'setObject:forKey:'". That's all well and good, and if you run it anyway, you'll get this output in the console:

-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object

Again, exactly what you'd expect to happen. However, at this point the app does not crash or terminate due to an uncaught exception. The setObject:forKey: method simply never returns, and the app appears to hang; the following NSLog is never executed. If you try to step over or into the method using GDB, debugging just seems to end, but without any explicit error message. The app continues to run, but the debugger provides no clue as to where in the code the execution is "stuck."

What's going on here? What is the app actually doing in this case, and why doesn't it crash with an NSInternalInconsistencyException or something of the like?

Edit: For those who have asked, I'm running XCode 4.1 on OS X Lion (10.7.2), building with "Apple LLVM compiler 2.1." I'm using all of the default settings you get with a new Cocoa project in XCode 4. I experience the same non-crashing behaviour regardless of whether I debug the program or just "Run" it. Changing from Debug building to Release building makes no difference. I can even locate the .app file manually in Finder and double click on it to execute it outside of XCode, and it still does not crash.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
Mitch Lindgren
  • 2,120
  • 1
  • 18
  • 36
  • 1
    Try running it not under the debugger; I bet it hard crashes. I find the debugger environment can have some unexpected effects on the runtime environment especially around exceptions and what happens when you hit them. – Ben Zotto Dec 14 '11 at 00:19
  • 2
    When the exception is thrown, control will resume with whatever catches the exception, *not* the next line after the exception. In your example either the debugger, or the bowels of the runtime, "caught" the exception. In any case you wouldn't expect the NSLog to happen after -setObject:forKey: threw an exception. – Vincent Gable Dec 14 '11 at 00:38
  • @VincentGable: Agreed, if the program crashed I wouldn't be the least bit surprised that the NSLog wasn't called. What surprises me is that it neither crashes nor advances to the next line. It seems like it should do one or the other. To those who have asked, I have updated the question with some additional details about the environment I'm running in. – Mitch Lindgren Dec 14 '11 at 03:39
  • Pretty much a duplicate of http://stackoverflow.com/q/3336278/341994 – matt Dec 15 '11 at 22:23

1 Answers1

1

Exceptions do not crash AppKit programs. NSApplication installs a default exception handler that catches exceptions that your code doesn't. Then you just go back into the runloop as normal.

Lots of apps exhibit this behaviour. It's a common cause of inexplicable blank views/windows. If an exception happens before a view manages to finish drawing, the view will be blank, but the app won't crash. Exceptions only cause a crash if you deliberately change the default exception handler to crash.

Bored Astronaut
  • 994
  • 5
  • 10
  • Thanks, this mostly answers my question. One thing I'm still confused about, though, is why I can't see the application return to the main event loop after the exception is caught. Or does that occur in a different thread? I'm not all that familiar with the inner workings of AppKit. – Mitch Lindgren Dec 17 '11 at 20:09