exceptions


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.