Exceptions The exception handling mechanism provides several powerful features found in languages like C++ or Java. 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: TRY { some code that can generate an exception } END_TRY CATCH(ExceptionClass1) { code to handle an exception of class ExceptionClass1 } CATCH(ExceptionClass2) { code to handle an exception of class ExceptionClass2 } ... OTHERWISE { catch all exceptions not handled above } END_CATCH 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: * you can go immediately after the `END_CATCH' statement if it does not issue any of `RETURN' or `RETRY' statements * you can reraise the exception by issuing the `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 You cannot execute a jump with the `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: TRY { some code that can generate an exception } END_TRY ... CLEANUP { sequence of code } END_CATCH the same sequence of code from inside the CLEANUP block 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: TRY { some code that can generate an exception } END_TRY CLEANUP { code to release acquired resources } END_CATCH You could use the `FINALLY' construct when the resource is acquired and also released in the same function. For example: acquire the resource TRY { some code that can generate an exception } FINALLY { code to release the resource } END_CATCH With these constructions the exception handling macros has the following syntax: TRY { some code that can generate an exception } END_TRY CATCH(ExceptionClass1) { code to handle an exception of class ExceptionClass1 } CATCH(ExceptionClass2) { code to handle an exception of class ExceptionClass2 } ... OTHERWISE { catch all exceptions not handled above } CLEANUP { ... } FINALLY { ... } END_CATCH OpenStep exceptions The OpenStep exceptions are defined in the terms of the above macros. #define NS_DURING TRY #define NS_HANDLER \ END_TRY \ OTHERWISE #define NS_ENDHANDLER END_CATCH 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.