Hour 21. Handling Exceptions and Errors


What You’ll Learn in This Hour

Image Using exceptions in today’s software

Image Throwing exceptions

Image Working with the try/catch/finally blocks

Image Logging incidents


Rethinking Exceptions and Errors

Most programming languages today provide some method for handling exceptions and errors—events that must interrupt the normal flow of processing because that normal flow of processing cannot handle the event that has occurred. Modern exception handling usually provides a structure that lets you react programmatically to the type of error and, if possible, take remedial action.

Compared with software from a decade or longer ago, today’s best software is much less verbose. Instead of reporting each problem as it occurs, software increasingly attempts to either prevent problems or to resolve them automatically. Sometimes problem resolution techniques are simple, such as automatically retrying a communication link three times before giving up and reporting a failure instead of reporting the problem on the first try. In cases such as this, you may well want to log the failed attempts even though you do not interrupt the user until you are certain you cannot recover. This makes debugging easier.

As software, operating systems, and hardware have evolved, people are doing more complex tasks with software than they were doing in many cases in the past, and, in many cases, they are doing those tasks with less support from traditional sources. The rise of crowdsourcing problem-solving sites, which was initially opposed by many corporate developers, is now widely accepted—because it often works.

All these changes in the computing environment mean that exception handling is, in many cases, focused on somehow completing either the task the user has asked for or a task that moves the user forward and stops at a point that is knowable and logical for both computer user and whatever support resources are available.


Note: Moving to a Logical and Knowable Place

In a large number of cases, when a user and app get into trouble, the issue can only be unraveled with the assistance of people with two separate skills, such as software engineering and domain knowledge. If an exception can be handled by moving to a place where either a software engineer or a domain specialist can resolve it, it can save a great deal of time and expense.


What most users desire is software that continues to behave as they expect it to. They do not want to even know about failures that they cannot resolve themselves or that do not matter. Today’s developers try to oblige.

The commonly used distinction between errors and exceptions is that errors can usually be recovered from—perhaps automatically. Exceptions may be fatal. Furthermore, errors often are caused by users, while exceptions are the result+ of action (or inaction) on the part of the developer. (These concepts are rooted in Java.) Apple’s Human Interface Guidelines for their various devices (particularly for the iOS devices) have a number of ideas on how to keep apps from visibly failing. Error Handling Programming Guide and Exception Programming Topics also provide additional information. In addition, searching for NSException and NSError on the developer site will reveal dozens of sample code projects.

Introducing the Exception and Error Classes

Two direct subclasses of NSObject are used in Cocoa and Objective-C to handle unexpected events. They have different structures and different uses. Exceptions and errors are described in the following sections. After that, you look inside the classes that implement them (NSException and NSError).

Although there are distinctions between exceptions and errors, ultimately the decision of which to use is up to you. This has become more important with the advent of 64-bit executables. The reason is that throwing exceptions in 64-bit is much more expensive than it was in 32-bit executables. On the other hand, @try blocks are much more efficient in 64-bit. Apple’s advice for 64-bit is that, “you should throw exceptions only when absolutely necessary.”

Using Exceptions

Exceptions are typically thrown (or raised) as the app runs. They include conditions such as attempting to access a nonexistent object or variable whether it is through an invalid array subscript or a dangling pointer. They also can arise from passing invalid arguments into methods.


Be Careful with Dynamism

A great deal of the power of Objective-C comes from its dynamic features. You can use id variables that can refer to any object in many cases. This lets you do remarkable things, such as write a method with an argument that you expect at runtime to be either an NSWindow, or an NSView. (Some object-oriented hierarchies link the two classes.) However, if you do so, the burden is on you and the developers who use your code to check that what actually is in that id variable at runtime is not a totally different object, such as an NSDocument. Many people believe that using id only when static typing is not feasible might be a good way to go—particularly if you are new to Objective-C. When you are comfortable with the dynamic features and the introspection methods such as the NSObject class method, you can relax.


You often cannot predict when these conditions will arise, but in many cases you can write brief snippets of code to test if they might arise before you do something (such as reference an object that might be nil) and then prevent them from arising. After an exception is raised, the app normally quits soon thereafter. It is common (but not necessary) to subclass NSException.

Using Errors

Unlike exceptions, errors are for issues that don’t require the app to quit. They have features allowing you to localize an error string so that you can present detailed error information to users. These include conditions such as low (or no) memory, a communication link that does not exist (or that has suddenly disappeared), and the like. Sometimes the app can simply retry the task; other times the user needs to be involved. There are cases where both strategies may be used. For example, in the absence of a communication link, the app may retry to connect three times. If that doesn’t work, the user is brought into the picture to try to troubleshoot the connection.


Note

“Error 17” is not detailed error information.


In keeping with the trend to not be verbose, the basic error message might not need to explain that three attempts have been made and failed, but that information can be logged so that users can access it if necessary.

Looking Inside NSException

Inside every NSException instance are three properties (one is optional):

Image name—This is an NSString that uniquely identifies the type of exception. (Methods of NSException—such as callStackSymbols—can identify where this particular exception occurred.)

Image reason—This is an NSString that contains a human-readable string explaining the exception. You can pass it along to users or use it as the basis for an NSError.

Image userInfo (optional)—This is an NSDictionary containing key-value pairs with additional information that is primarily for developers to use in choosing what actions to take to ameliorate or bypass the exception. It also can be used to log the problem in the console.

GO TO ImageReading and Writing Dictionaries” in Hour 15, p. 213, for more on dictionaries and how they can be used to pass structured data through as a single object in a method call.

Looking Inside NSError

Three different properties are inside NSError (two are required, and one is optional):

Image code—This is an NSInteger that identifies the error (the values for the Cocoa domain are shown in Table 21.1).

Image

TABLE 21.1 NSError Domain Codes

Image domain—This NSString identifies the error’s domain (the values are shown in Table 21.2).

Image
Image
Image

TABLE 21.2 NSError Error Codes in the Cocoa Domain

Image userInfo (optional)—This is an NSDictionary that may contain key-value pairs further describing the error.

In addition, four methods provide localized assistance for users:

Image localizedDescription (NSString)

Image localizedRecoveryOptions (NSArray)

Image localizedRecoverySuggestion (NSString)

Image localizedFailureReason (NSString)

(For more information, consult the NSError class reference in Xcode or on developer.apple.com.)

These let you localize the description, recovery options (these are presented as button names), as well as a suggestion and reason for the failure.


Using Errors in Cocoa and Cocoa Touch

Although the internal structure of NSError is the same on Cocoa and Cocoa Touch, you use it in different ways in part due to the difference in the interfaces. The documentation for each OS on developer.apple.com provides you with details of using NSError with sheets and alerts (on OS X) and with pop-overs (on iOS).

With both NSException and NSError, the structure that you set up for name values (NSException) and domain values (NSError) is crucial. With the right sets of names and domains, you can use the userInfo dictionary to refine the problem that has been encountered.

Many of the domain values for NSError are shown in Table 21.1. The error codes for the Cocoa domain are shown in Table 21.2. As you scan through the error codes, you will see the types of errors that can be caught. (Note that some of them are placeholders for ranges of errors; they are the error codes that contain minimum and maximum to delimit the range.) It’s a fact of development life that as more features and frameworks are added to Cocoa and Cocoa Touch, more domain values and error codes are added.


Identifying an Exception

The most common exception pattern in Objective-C (and many other languages) is based on try/catch/finally blocks. The conceptual structure is shown in Listing 21.1.

LISTING 21.1 Objective-C Try/Catch Block


@try {
    // code that could fail
}
@catch (NSException *exception) {
    // code to perform if there has been a failure
}
@finally {
      // code that is performed regardless of success/failure
}
@end


This is the conceptual structure. There is a code snippet for an Objective-C try/catch/finally block that you can drag from the Code Snippet library into your code, as shown in Figure 21.1.

Image

FIGURE 21.1 Drag a try/catch snippet from the library into your code.


Tip

Make sure you pick up the Objective-C snippet because there is also a C++ snippet. The snippet contains placeholders that you replace with your own code. As you do that, the errors you see in Figure 21.1 will disappear.


The @try block contains code that might raise or throw an exception. This can happen because you deliberately raise or throw it based on a condition that you have found in your code or the environment, or it can happen because an exception is thrown while control is in that block, but it is thrown by something other than an explicit action in your code. For example, if a line of code in your @try block calls a method that calls another and another, an exception may be thrown several calls away when, perhaps, a dangling pointer is dereferenced. The exception is passed up the exception chain until it finds an @catch block that traps it. All of this is managed for you; all you have to do is declare the @try block along with @catch and @finally.

@finally is the code that is performed at the end of the block regardless of success or failure. It may set a value—perhaps from temporary values set in @try and @catch.


Tip

Note that @finally can be an empty block.


Throwing an Exception

Throwing (or raising) an exception is a two-step process:

Image Create an NSException instance (or an instance of a subclass).

Image Throw (or raise) it.

For many people, the simplest way to create an NSException instance is to use the class factory method:

+ (NSException *)exceptionWithName:(NSString *)name
  reason:(NSString *)reason
  userInfo:(NSDictionary *)userInfo

There are other techniques described in the NSException class reference on developer.apple.com and in Xcode Help.

Catching an Exception

You catch an exception in an @catch block, as shown previously in Listing 21.1. You can have several of these blocks with each one catching a separate exception. They are selected at runtime based on the class of the exception. As noted previously, you can subclass NSException for different exceptions. Each @catch block handles one class or subclass of NSException. The last block may handle a totally generic id exception. In addition to handling different exceptions in different ways, you can also create a new NSException instance and throw it. In that way, you can transform one exception into another.

GO TO Image Hour 23, “Working with the Debugger,” p. 293, to find out about using the log and NSConsole. They are used with errors and exceptions as well as the debugger.

Summary

This hour has shown you the ways to handle exceptions that are typically fatal issues and errors that can often be worked around. As noted, the distinction is conceptual and practical—exceptions are more expensive to use than errors on 64-bit architectures.

Q&A

Q. What changes in error handling have occurred in the last few years?

A. Apps are becoming less verbose as they handle issues on their own without bothering users.

Q. How can you prevent exceptions?

A. Typical methods are to check for dangling or nil pointers before dereferencing them and making certain that objects exist. If you are worried about performance, test with and without these small tests. Typically, unless you are in a very tight loop, there is no reason not to make these basic tests.

Workshop

Quiz

1. How do you implement separate @catch blocks?

2. Should you quit after posting an error alert to a user?

Quiz Answers

1. Use different subclasses of NSException and NSException itself and create a catch block for each one.

2. Typically, exceptions are followed up by the app quitting. With errors, the user can often continue.

Activities

Browse discussion boards for apps and review the comments about error handling and what people do not like about it in various apps. This should not be difficult. Error handling is a common complaint, and you should be able to see what really bothers people.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.24.106