15 Most Recent [RSS]
Breaking on ObjC exceptions - the objc_exception_throw breakpoint
Going over videos from NSConference I hadn't watched yet, I just saw one neat little trick Mike Lee shared while me and a few others were outside preparing a later presentation. It's a trick I used a lot, and Mike mentioned a few things but forgot a few others, so I thought I'd re-iterate it here and add my two cents' worth:
In Xcode, in the Breakpoints window, you can double-click the bottom-most entry and just type in any function of method name. I.e. you can type in [NSWindow orderOut:] to diagnose why your window disappears. Also, as long as Xcode has a "break on C++ exception" menu item, but not a "break on ObjC exception" menu item, you may want to add a breakpoint on objc_exception_throw (or if you're debugging a problem on Mac OS X 10.4 or earlier, on [NSException raise]).
Generally, you can just leave that breakpoint there and activated, or even put it into your .gdbinit file as a fb objc_exception_throw statement, since, as Mike notes, the Cocoa frameworks really only use exceptions for programming error situations, like array-out-of-bounds accesses.
However, there are a few notable exceptions here, and they're getting bigger as time progresses:
- Distributed Objects: When a call to a distributed object proxy encounters an error, e.g. a broken network connection, it throws an exception. So, when you use DO, it's generally a good idea to keep code that calls into these objects segregated from the rest of the app, and to catch exceptions from it and deal with them in a sensible way. Since exceptions may occur as part of your regular program flow, you also wouldn't want to have the breakpoint always on, as you'd find yourself in the debugger way too often.
- Scripting Bridge: The situation is essentially the same as for DO: If you for example ask for album art from an iTunes track, and the track has no art, you get an exception. Sadly, there's no way to find out about this beforehand, so if you're talking to an app that works like that, catch exceptions selectively.
- The modern 64-bit runtime: The 64-bit runtime (and the modern 32-bit runtime on the iPhone, but not on the iPhone simulator) unifies Objective C exceptions and C++ exceptions. As such, a breakpoint on C++ exceptions is one on ObjC exceptions, and vice versa. C++ uses exceptions differently than ObjC. It is common to throw exceptions in C++ and then catch them and recover from them, and many system libraries (e.g. OpenSSL) actually do this internally. In C++, that's hardly a problem, as you generally select the type of exception you want to crash or watch for, but ObjC doesn't really have that. If you set a breakpoint on exceptions, it will fire for all of those, making this feature essentially useless for Objective C programmers.
Nonetheless, it's useful to break on an exception: You will be brought into the debugger, and you will see a stack backtrace of the code that caused the exception, and see the state all variables are in. That makes it a lot easier to diagnose a programming error and fix it.
Other useful breakpoints I keep in my .gdbinit:
The last three are specific to garbage-collected ObjC.
Another one: bindings with the NSRaisesForNotApplicableKeysBindingOption set to NO. The exception is actually still thrown, the bindings mechanism just catches it. This is an unusual one, because most of the time your available keys don't change, but if you do have this setup it's rather annoying.