Learn Objective-C on the Mac - Mark Dalrymple [128]
Note that we can print the value of an NSString using the po command, and print the value of any basic C type using the p command. Often when using the p command, gdb will complain that it doesn’t know the type of the value you’re trying to display, so we put a little (int) just before the method call, which tells gdb what to expect.
Now we know the error code and domain. A quick look in FoundationErrors.h turns up this info:
That still might leave you wondering how this hangs together. As you recall, when you create an NSError, you specify a domain, a code, and optionally a dictionary containing additional info. This dictionary is a key piece of the puzzle. If the dictionary you pass into this method has a value for a key called @“NSLocalizedDescriptionKey”, then that value will be returned whenever localizedDescription is called.
So, let’s see what the userInfo dictionary contains:
Um. Apparently that’s not the answer, either. The userInfo dictionary doesn’t specify a localized description, but it does include a couple of other things: the path to the file we were trying to access, and what looks like another NSError. An error wrapped inside an error! Let’s see what that inner error’s domain, code, userInfo, and localizedError look like. We start off with another gdb command, the x command, which simply executes the statements on the rest of the line, without printing a result like the po or p commands do. In this case, we’re assigning the address of the inner error object to a new variable we’re calling $inner. This is a nice feature of gdb that lets us hang on to the results of queries we execute, letting values stick around, and saving us some typing later on:
After assigning the object’s address to the new variable, gdb outputs both the address of our new variable, and its content (the address of the object it’s pointing at), but you can ignore that. From here on out, we can refer to that inner error as just $inner, like this:
This presents us with yet another riddle. When we look at the underlying error, which has no userInfo dictionary at all, it still presents a human-readable localizedDescription that doesn’t seem immediately apparent just by looking at the domain and code. In fact, you can make your own inner and outer errors like this:
Those errors will behave exactly like the ones generated by NSFileManager, including displaying human-readable text that we’re not specifying anywhere! As it turns out, the source of this magic seems to be the NSError class itself. It apparently has enough built-in specific knowledge about Cocoa’s error domains and error codes to generate meaningful sentences about many of the errors that a program is likely to encounter. This means that in general, the errors provided to you by Cocoa method calls won’t need to be prettied up in order to be shown to users.
Presenting an Error
And how about showing errors to users, while we’re at it? Here we’ve been calling [NSApp presentError:e] to make the application display a modal window, but there are some other choices. In truth, the presentError: method is implemented in NSResponder, NSDocument, and NSDocumentController, which means that whenever an error occurs, you can pass it along to the nearest or most relevant object around, at which point it is passed along a chain of responsibility, similar to the responder chain, until some object actually displays the error. This opens up possibilities for displaying error messages in a different fashion, such as with a document-modal sheet alert.
In Conclusion
You’ve now seen the primary ways that a Cocoa app can deal with various kinds of unpleasantness, how to avoid some common mistakes, and how to use gdb to help track down problems. You’ve also seen how methods can use NSError instances to deal with problems in a controlled manner. All of this will come in handy as you delve further into Cocoa programming.
Chapter13
Drawing