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

Handling an NSException

To see how an exception handler process an NSException, first we need a source of exceptions. Listing 4 shows a method named divideLong:by:. This method takes two NSNumbers as its input arguments. It then checks aDivisor to see if it has a zero value. If true, the method raises an instance of NSInvalidArgumentException. Otherwise, it divides the two numbers and returns the result as an NSNumber.

Listing 4. Dividing two NSNumbers
- (NSNumber *)divideLong:(NSNumber *)aDividend by:(NSNumber *)aDivisor
{
    NSException *loc_err;
    long     loc_long;
    
    // validity check
    loc_long = [aDivisor longValue];
    if (loc_long == 0)
    {
        // create and send an exception signal
        loc_err = [NSException 
            exceptionWithName:NSInvalidArgumentException
            reason:@"Division by zero attempted" 
            userInfo:nil];
        [loc_err raise];
    }
    else
        // perform the division
        loc_long = [aDividend longValue] / loc_long;
    
    // return the results
    return ([NSNumber numberWithLong:loc_long]);
}

To trap the NSException, write the exception handler as shown in Listing 5. The @try block first assigns integer values to two NSNumber locals. Next, it passes those same locals to the divideLong:by: method. It then stores the result of the method into another NSNumber local, and displays it using the NSLog() function. But if an exception occurs, the @try block transfers control to the @catch block, which then displays the exception using its NSLog() function.

Notice that the @finally block is included as a placeholder. Even though it is optional, it is good practice to have it around should you decide to add any clean-up code. Feel free to exclude the block if it makes your code more readable.

Listing 5. Trapping a single NSException
NSNumber *tst_dividend, *tst_divisor, *tst_quotient;
// prepare the trap
@try
{
    // initialize the following locals
    tst_dividend = [NSNumber numberWithLong:8];
    tst_divisor = [NSNumber numberWithLong:2];
    
    // attempt a division operation
    tst_quotient = [self divideLong:tst_dividend by:tst_divisor];
    
    // display the results
    NSLog (@"The answer is: %@", tst_quotient);
}
@catch (NSException *theErr)
{
    // an exception has occured
    // display the results
    NSLog (@"The exception is:\n name: %@\nreason: %@"
        , [theErr name], [theErr reason]);
}
@finally
{
    //...
    // the housekeeping domain
    //...
}

Run the code in Listing 5 as is. It will display the following message at the Xcode console window.

2006-11-26 03:52:30.004 DemoApp[506] The answer is: 4

Next, change the code so that tst_divisor gets a zero value.

tst_divisor = [NSNumber numberWithLong:0];

Run the example code again. This time, the local object tst_quotient remains set to a nil. Also, the NSLog() function in the @try block is skipped over. Instead, the NSLog() function in the @catch block will display the following console message.

2006-11-26 04:06:03.654 DemoApp[569] The exception is:
    name: NSInvalidArgumentException
    reason: Division by zero attempted

Handling Multiple NSExceptions

The basic handler can also trap multiple NSExceptions. To see how this is done, first change the divideLong:by: method as shown in Listing 6. This time, the method checks if one of its input arguments is a nil. If this is true, the method raises an instance of NSInternalInconsistencyException.

Listing 6. Generating multiple NSExceptions
- (NSNumber *)divideLong:(NSNumber *)aDividend 
        by:(NSNumber *)aDivisor
{
    NSException *loc_err;
    long loc_long;
    
    // validity check
    if ((aDividend == nil) || (aDivisor == nil))
    {
        // create and send an exception signal
        loc_err = [NSException 
            exceptionWithName:NSInternalInconsistencyException
            reason:@"Nil input arguments are sent" 
            userInfo:nil];
        [loc_err raise];
    }
    else
    {
        loc_long = [aDivisor longValue];
        if (loc_long == 0)
        {
            // create and send an exception signal
            loc_err = [NSException 
                exceptionWithName:NSInvalidArgumentException
                reason:@"Division by zero attempted" 
                userInfo:nil];
            [loc_err raise];
        }
        else
            // perform the division
            loc_long = [aDividend longValue] / loc_long;
    }
    // return the results
    return ([NSNumber numberWithLong:loc_long]);
}

Then change the @catch block as shown in Listing 7. The block now checks the name property of the NSException, theErr. It then displays the right log message for that name. If the block failed to process the NSException, it sends a raise message to send the NSException to the next handler.

Listing 7. Trapping Multiple NSExceptions
NSNumber *tst_dividend, *tst_divisor, *tst_quotient;
NSString *tst_name;

// prepare the trap
@try
{
    // initialize the following locals
    tst_dividend = [NSNumber numberWithLong:8];
    tst_divisor = [NSNumber numberWithLong:2];
    
    // attempt a division operation
    tst_quotient = [self divideLong:tst_dividend 
                by:tst_divisor];
    
    // display the results
    NSLog (@"The answer is: %@", tst_quotient);
}
@catch (NSException *theErr)
{
    tst_name = [theErr name];
    if ([tst_name  isEqualToString:NSInvalidArgumentException])
        NSLog (@"The answer is: INFINITY");
    else 
    {
        if ([tst_name isEqualToString:NSInternalInconsistencyException])
            NSLog (@"The answer is: UNDEFINED");
        else
            [theErr raise];
    }
}
@finally
{
    //...
    // the housekeeping domain
    //...
}

Run the example code as is. It will still display the following message on the console window.

2006-12-06 03:52:30.004 DemoApp[506] The answer is: 4

Next, set the variable tst_divisor to zero. The code then displays a different message to the window.

2006-12-06 03:55:30.014 DemoApp[506] The answer is: INFINITY

Finally, comment out the following two lines of code. This sets the variables tst_dividend and tst_divisor to nil.

// comment these lines out
// tst_dividend = [NSNumber numberWithLong:8];
// tst_divisor = [NSNumber numberWithLong:2];,

Now, run the modified code. It displays another message to the console window.

2006-12-06 04:10:10.040 DemoApp[506] The answer is: UNDEFINED

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

Next Pagearrow