Exception handlers can be nested so that an exception raised in an inner domain can be treated by the local exception handler and any number of encompassing exception handlers. This design allows an exception to be handled by code that, although it is further from the code actually generating the exceptionö, might have more knowledge about the conditions leading to the exception.
You can nest exception handlers using both the @try...@catch...@finally
directives and the NS_DURING...NS_HANDLER...NS_ENDHANDLER
macros. There are some subtle differences in these mechanisms between the flow of program control from inner exception handler to outer exception handler, so the following sections discuss them separately.
Important: The compiler directives discussed below were introduced in Mac OS X v10.3. An application that uses these directives for exception handling cannot run on earlier versions of the operating system.
To understand how nested exception handlers defined with the compiler directives are invoked, consider the code fragment in Listing 1.
Listing 1 Throwing and rethrowing an exception
@try { |
// ... |
if (someError) { |
NSException *theException = [NSException exceptionWithName:MyAppException reason:@"Some error just occurred!" userInfo:nil]; |
@throw theException; |
} |
} |
@catch (NSException *exception) { |
if ([[exception name] isEqualToString:MyAppException]) { |
NSRunAlertPanel(@"Error Panel", @"%@", @"OK", nil, nil, |
localException); |
} |
@throw; // rethrow the exception |
} |
@finally { |
[self cleanUp]; |
} |
In this code the exception (exception
) is thrown again at the end of the local handler, allowing an encompassing exception handler to take some additional action. Figure 1 illustrates the flow of program control between nested exception handlers created with the @catch
directive.
An exception raised within Method 3's domain causes execution to jump to its local exception handler. In a typical application, this exception handler queries the exception object to determine the nature of the exception. The local handler may handle exception types that it recognizes and then may rethrow the exception object to pass notification of the exception to the handler nested above it—that is, the handler in Method 2. However, before the next outer exception handler is invoked, the code in the @finally
block associated with the local exception handler is executed. (This has implications for memory management, as discussed in “Exception Handling and Memory Management .”)
An exception that is rethrown appears to the next higher handler just as if the initial exception had been raised within its own exception handling domain. Method 2's exception handler again may handle the exception and may rethrow the exception to Method 1's exception handler; Method 1’s handler does not receive the rethrown exception until Method 2’s @finally
block completes its task. Finally, Method 1’s handler rethrows the exception. Because there is no exception handling domain above Method 1, the exception passes to the uncaught exception handler (see “Uncaught Exceptions”).
You program should use the exception-handling macros if it must be compatible with versions of the operating system prior to Mac OS X v10.3. If you are using the exception-handling macros, the code equivalent to that in Listing 1 would look something like Listing 2.
Listing 2 Raising and re-raising an exception
NS_DURING |
... |
if (someError) { |
NSException *theException = [NSException exceptionWithName:MyAppException reason:@"Some error just occurred!" userInfo:nil]; |
[theException raise]; |
} |
[self cleanUp]; |
NS_HANDLER |
if ([[localException name] isEqualToString:MyAppException]) { |
NSRunAlertPanel(@"Error Panel", @"%@", @"OK", nil, nil, |
localException); |
} |
[self cleanUp]; |
[localException raise]; /* Re-raise the exception. */ |
NS_ENDHANDLER |
In this code the exception (exception
or localException
) is raised again at the end of the local handler, allowing an encompassing exception handler to take some additional action. Figure 2 illustrates the use of nested exception handlers, and is discussed in the text that follows.
An exception raised within Method 3’s domain causes execution to jump to its local exception handler. In a typical application, this exception handler checks the object localException
to determine the nature of the exception. For exception types that it recognizes, the local handler responds and then may send raise
to localException
to pass notification of the exception to the handler above, the handler in Method 2. (An exception that is re-raised appears to the next higher handler just as if the initial exception had been raised within its own exception handling domain.) Method 2’s exception handler does the same and then re-raises the exception to Method 1’s handler. Finally, Method 1’s handler re-raises the exception. Since there is no exception handling domain above Method 1, the exception is transferred to the uncaught exception handler (see “Uncaught Exceptions”).
Last updated: 2010-02-24