Understanding Exceptions and Handlers in Cocoa
Pages: 1, 2, 3, 4, 5, 6, 7
Handling Other Cocoa Objects
Finally, the basic handler can trap other Cocoa objects besides an NSException. Simply use the @throw keyword to send the Cocoa object to the handler. Then use the @catch keyword to trap that object.
To demonstrate, modify the divideLong:by: method as shown in Listing 8. The method stores the division results into the NSNumber local, loc_quot. Then it uses the @throw keyword to send the result to the handler. Notice that the return type for the method is changed to a void. Compare this method to those shown in Listing 4 and Listing 6.
Listing 8. Using the @throw directive
- (void)divideLong:(NSNumber *)aDividend
by:(NSNumber *)aDivisor
{
NSException *loc_err;
NSNumber *loc_quot;
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 using @throw
loc_quot = [NSNumber numberWithLong:loc_long];
@throw loc_quot;
}
}
}
Now, modify the exception handler as shown in Listing 9. The second @catch block has an NSNumber as its argument. It retrieves the value of the argument and displays it using the NSLog() function. Also, if you run the modified code, you will see the same set of messages in the Xcode console window.
Listing 9. Trapping a thrown Cocoa object
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];
}
}
@catch (NSNumber *theNum)
{
// display the number results
NSLog (@"The answer is: %@", theNum);
}
@finally
{
//...
// the housekeeping domain
//...
}
Using the @throw keyword to return results has its own drawbacks.
- It can make the code hard to debug and maintain. After all, the
@throwkeyword works in the same way as a goto statement. - Throwing an exception uses a lot of resources. Unwanted use of exceptions can give a software product a large memory footprint. It may also cause the product to perform slower.
When Handlers Are Nested
Sometimes, an exception handler is placed within another. This placement, called nesting, can happen either by consequence or by design. Learning how control flows inside nested handlers can help in debugging a faulty handler or in tracing an error signal.
Types of Nesting Structures
Exception handlers can be nested in three ways.
- One handler is nested in another within the same method.
- Both handlers are in different methods, which are in the same class. Then the handler in the first method calls the second method, which has the other handler.
- Both handlers are in different methods, and the methods are in different classes. Then the method in the first class calls the method in the second class.
Listing 10 is an example of the first nesting structure. Here, demoMethod has two exception handlers. The outer @try block of the first handler contains the @try…@catch…@finally block of the second handler. This type of nesting often happens by design.
Notice that the two @catch blocks use different names for their NSException variable. This allows the handlers know which NSException to trap at that time. In the example shown, the inner handler traps only NSExceptions with the ID Inner Error, while the outer handler traps those with the ID Outer Error.
Listing 10. Handlers nested in the same method
- (void)demoMethod
{
// ...
// code before the handler
// ...
// start of the outer handler
@try
{
// ...
// code that could generate an outer exception
// ...
// start of the inner handler
@try
{
// ...
// code that could generate an inner exception
// ...
}
@catch (NSException *theErr2)
{
// identify the exception
if ([[theErr2 name] isEqualToString:@"Inner Error"])
// ...
// code to handle the inner exception
// ...
else
[theErr2 raise];
}
@finally
{
// ...
// finish the inner handler
// ...
}
}
@catch (NSException * theErr1)
{
// identify the exception
if ([[theErr1 name] isEqualToString:@"Outer Error"])
// ...
// code to handle the outer exception
// ...
else
[theErr1 raise];
}
@finally
{
// ...
// finish the outer handler
// ...
}
}

