Learn Objective-C on the Mac - Mark Dalrymple [118]
Let’s take a look at an example. Imagine the following method, placed in a class that has a settable name:
Now imagine that we want to make setting the name an action that can be undone. This is easily accomplished by adding the lines shown in bold below:
Note that we left out the part of the code where we actually acquire the undo manager. In an actual application, the undo manager can come from one of several places, depending on whether or not the app has Core Data and NSDocument support. In a Core Data app, you can always ask the shared NSManagedObjectContext object for an undo manager, and in an app with both Core Data and Document support each document has its own undo manager.
The ultimate expression of this occurs in a Core Data application, which actually implements something like the above on your behalf. If you look at the setName: method above, you can see that it’s pretty formulaic. Core Data implements some magic behind the scenes so that you don’t have to implement that setName: method. As soon as any edit occurs to a model object, Core Data notices and sets up the undo for you.
In Conclusion
Now you’ve created your first NSDocument-based application from the ground up, and learned a bit about drawing to an NSView and dealing with colors, all of which are useful skills for a variety of application areas. In later chapters, we’ll build on those skills even more, especially the fun graphics programming where Cocoa really shines, but in the next chapter we have to take a break from the fun and learn what happens when things go wrong in your apps, and how to deal with them, using NSError and NSException.
Chapter12
Exceptions, Signals, Errors, and Debugging
Anyone who’s done any sort of programming knows that sometimes, things just don’t work out as planned. You forget to handle a specific edge case, or a system call fails in a way that’s never occurred to you, and suddenly your program blows up in your face. Every programming language and development environment has ways of dealing with these problems, and Cocoa is no exception. In this chapter, you’ll learn about Cocoa’s mechanisms for creating and handling exceptions and errors, two similar-sounding but conceptually very different systems. You’ll learn the different ways that each of them is used, how to handle them, and how to initiate them yourself. You’ll also learn how certain memory abuses can cause signals to occur, typically resulting in a crash. And, we’ll take a peek at the debugger built into Xcode which can help you tackle these problems.
Exception Handling
Let’s start off with exception handling. An exception is a special object that can be created in one part of a program in order to tell another part that something has gone wrong; this is called “raising” an exception, and in code it might look something like this:
In this case, we’re creating and raising an exception in a single step, using a class method of the NSException class. The first parameter to this method is a string for the exception’s name (in this case NSRangeException, which is a predefined exception name defined in Cocoa), which allows for general categorization of exceptions. Unlike many other exception-handling environments, NSException is seldom subclassed, and so its name is used to differentiate between different types of exceptions. The second parameter we pass is a format for the exception’s “reason,” a human-readable descriptive string. This format is the same sort of list of items you can pass to NSLog, where the first is an NSString and the others are interpolated as specified by the string.
Catching Exceptions
When that code is executed, if index < 0, the program’s normal flow is interrupted. Instead of continuing with the rest of the method, the program will start working its way down the call stack, looking for a special code construct called an “exception handler.” An exception handler will execute all code contained within a pair of curly-braces prefaced by the @try keyword