The exception handling mechanism provides several powerful features found in languages like C++ or Java. It extends the normal OpenStep exception handling, while still maintaining the backward compatibility with this. A program using the extended exception handling mechanism is still able to work with the normal OpenStep exception handling and viceversa.
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 exception handlers 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
localException. 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
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 in 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
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 implemented
for the programs compiled with the NeXT runtime.
Go to the first, previous, next, last section, table of contents.