The first powerful feature is that you have exceptions grouped by classes. This means you can group exceptions in a hierarchy; not only a tree like but even as a general graph. This feature is present in C++ and with some restrictions in Java.
Another feature is that the exception handler is called as a function. So if you are in debugger you can see the whole stack frame from the point that generated the exception up to the exception handler. So you are able to see what were the conditions that made the exception to be raised.
The actual mechanism is based on nested functions, an extension to the C language. This extension is currently supported by the GNU C Compiler. The actual mechanism is written using macros, so there is no need for special support from the compiler.
A code written using these macros looks like this:
In the TRY
block the code that is supposed to generate an exception is executed. You can nest TRY
blocks by entering other blocks of code in TRY
blocks. All TRY
blocks form a stack with the most recent TRY
block which is executing on the top of the stack.
If nothing happens during the execution of code inside a TRY
block, the program continues with the first statement after the END_CATCH
label. When this thing happens the topmost TRY
block is popped off the stack.
To generate an exception you should allocate an exception object and pass it to the THROW
function. This is called raising an exception. When an exception is raised the exception handler blocks are searched for the one that can `catch' the exception. This means to find the first CATCH
block that match the exception object class. This matching is done by sending the exception object the exceptionIsKindOf:
message having as argument the class specified as parameter to CATCH
block. If the result of this message is true then the CATCH
block matches the exception.
The exceptionIsKindOf:
method is implemented at the NSException class to return the result of isKindOf:
. So implicitly the exceptions are grouped after their inheritance hierarchy. Some specific exception classes could implement the exceptionIsKindOf:
method and simulate a graph inheritance, which is like the multiple inheritance in C++.
Inside the CATCH
and OTHERWISE
blocks you have one hidden parameter, the exception object, which has the name exception
. You can do the following actions inside an exception handler:
END_CATCH
statement if it does not issue any of RETURN
or RETRY
statements
RERAISE
if you want to generate an exception with the same object, or you can use THROW
to generate an exception with a different object
goto
statement from the TRY
, CATCH
or OTHERWISE
blocks outside them. These jumps are permitted only locally in the block. Also, do not return from TRY
or CATCH
blocks with ordinary return statements..
You can jump outside the TRY
block with the BREAK
statement that will go to the first statement following the END_CATCH
statement.
Another construction allows you to specify a block of code that will be executed when an exception is caught by an upper exception handler. This allows you to do some cleanup, for example to release all the resources acquired from the system (such as memory allocation or file descriptors). This block is introduced by the CLEANUP
label.
Another construction is the FINALLY
block. It is equivalent with the finally
block from Java. The code inside this block is called whenever either an exception is raised and caught by an upper handler or when the code inside a TRY
block runs successfully.
The FINALLY
construct is equivalent with the following CLEANUP
construct:
If several exception handlers are nested the order in which the cleanup and finally blocks are called is the same with the order in which the functions containing them will normally return.
There are situations when you acquire some resources and you want to be sure that they are released in case of an exception that is caught above you in the stack frame. So you don't need the CATCH
or OTHERWISE
blocks. You could simply write:
You could use the FINALLY
construct when the resource is acquired and also released in the same function. For example:
With these constructions the exception handling macros has the following syntax:
In the actual implementation you can also use the NS_VALRETURN
and NS_VOIDRETURN
macros inside the TRY
block, respectively inside NS_DURING block.
When you use NS_VALRETURN
or NS_VOIDRETURN
macros inside a TRY
block, be aware that before the function returns, the code inside all FINALLY
blocks associated with the TRY
block are executed first. However, because of a bug in the GNU compiler that causes the compiler to crash when it compiles Objective-C programs with nested functions for the NeXT runtime, this behavior is not performed in the programs compiled for the NeXT runtime.