macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Understanding Exceptions and Handlers in Cocoa
Pages: 1, 2, 3, 4, 5, 6, 7

Listing 2 shows how demoMethod deals with variable input arguments. If you send the message [aClass demoMethod:23, 64, 256], the class raises an NSException with the name "Invalid arguments". The reason property of the instance is set to the string "Arguments List = 0x40, 0d256". Its userInfo property is still set to a nil.

Listing 2. Handling variable arguments
- (void)demoMethod:(int)theCount,...
{
    NSException    *anException;
    NSString    *aFormat;
    va_list        aList;
    
    // prepare to access the variable input arguments
    va_start(aList, theCount);
    //...
    // something happened to cause an exception
    //...
    aFormat = [NSString stringWithString:@"Argument List = 0x%x, 0d%u"];
            [NSException raise:@"Invalid arguments" 
            format:aFormat
            arguments:aList];
    //...
}

Predefined Exceptions

The Cocoa framework comes with a large set of predefined exception names. These unique NSString constants describe specific error states. Most, if not all, Cocoa classes use these predefined names when they send their exception signals.

The framework that shipped with Xcode 2.4 has at least 60 predefined exception names. Most of the names are listed in two header files: NSException.h and NSErrors.h. Describing every predefined name is beyond the scope of this article, but you can find a list of these names in the Foundation Constants document, which is part of the ADC Reference library.

Check the list for a predefined name that describes your code's error state. Once you find the right name, use it to raise an exception for that state. For instance, to report an out-of-state error, use an NSRangeException. The following sample code first creates an instance of NSException using the NSRangeException name. It then passes a raise message to the instance to send it on the way.

anException = [NSException exceptionWithName:NSRangeException 
        reason:@"Attempted to read beyond the string."
        userInfo:nil];
[anException raise];

To report a write-to-text error, use an NSTextWriteException. The sample code below uses a raise:format, message to create an instance of NSException. It sets the name property of the instance to NSTextWriteException, and the reason property to the string "Tried to write to the file..."

[NSException raise:NSTextWriteException 
    format:@"Tried to write to the file %@"
    , aFileName];

Using a predefined name keeps the code consistent and predictable. It allows other Cocoa classes to correctly identify and process the raised exception. Make sure to use the reason and userInfo properties to differentiate your code from those raised by the framework.

Handling Exceptions in Cocoa

The aim of an exception handler is to quietly trap and process an incoming exception. At best, users should be unaware that an error occurred, unless they prefer to be. At worst, users get a modal dialog that warns them of the fatal error (Figure 2).

A typical fatal error dialog
Figure 2. A typical fatal error dialog

There are two ways to write exception handlers in Cocoa. One way is to use NS_ macros, the other is to use the new Objective-C keywords: @try, @catch, and @finally. Handlers written with these keywords are supported only on version 10.3 and newer of Mac OS X. Handlers written with NS_ macros, however, are supported by all versions of Mac OS X.

Apple recommends using the Objective-C keywords to write exception handlers. For that reason, this article will focus only on that approach. But if you want to learn about NS_ macros, read its entry in the ADC document on exception programming.

The Basic Exception Handler

Listing 3 shows the basic structure of an exception handler. The @try keyword sets the start of the exception-handling block. This block has code that may generate an exception. The @catch keyword sets the start of the local handler block. This block has code that will process the trapped exception. It also has code that can resend the exception when required. The @finally keyword sets the start of the housekeeping block. This block has code that performs any clean-up tasks needed. Unlike the first two blocks, this block is optional.

Listing 3. A basic exception handler
@try
{
    //...
    // the exception handling domain
    //...
}
@catch (NSException *theErr)
{
    //...
    // the local exception handler
    //...
}
@finally
{
    //...
    // the housekeeping domain
    //...
}

Figure 3 shows the flow of control within an exception handler. If an exception occurs, the @try block transfers control to the @catch block. Once the @catch block finishes, it transfers control to the @finally block. But if the @try block finishes without any exceptions, it also transfers control to the @finally block. In short, the @finally block is always executed whether or not an exception has occurred.

The flow of control within the handler
Figure 3. The flow of control within the handler

To use the new keywords in your Xcode project, first choose Edit Project Settings from the Project menu. From the Project Info dialog, click on the Build tab to display a list of build settings. Then choose Language from the drop-down list Collection. Scroll down the list until you see an entry for Enable Objective-C Exceptions (Figure 4). Click on the check box to select this option.

Enabling the compiler keywords
Figure 4. Enabling the compiler keywords

If you are using an older version of Xcode, scroll down the list until you see an entry for Other C Flag (Figure 5). Click on the cell next to that entry and type -fobjc-exceptions. Xcode will send this setting to GCC each time it builds the Cocoa project.

Setting the option flags
Figure 5. Setting the option flags

Pages: 1, 2, 3, 4, 5, 6, 7

Next Pagearrow