Many modern languages have exceptions. If you come from Java you’ll most likely using exceptions all the way to handle error cases. For objective-C you are going to have to go back and forget everything you knew about exceptions and start from scratch. Even the terms exception and error are used conversely.
The first thing to note is that Automatic Reference Counting (ARC) is not exception safe by default. In practice, this means that any objects that should be released at the end of a scope in which an exception is thrown will not be released. With the compiler flag -fobjc-arc-exceptions it is possible to enable exception-safe code to be generated. However this introduces extra code that has to be run even for the scenario in which no exceptions are thrown.
Objective-C has taken the approach to use exceptions for the rare scenario in which recovery should be avoided and an exception cause the application to exit. This means that complex exception-safe code with resource releasing does not have to be included. Exception are to be used for fatal errors only i.e. when a method of an abstract class is not implemented.
- (void)abstractMethod {
NSString *reason =
[NSString stringWithFormat:
@"%@ must be overridden", NSStringFormSelector(_cmd)]
@throw [NSException exceptionWithName: NSInternalInconsistencyException
reason: reason,
userInfo: nil];
If exceptions are about to be used only for fatal error, what about other errors? The
paradigm chosen by Objective-C to indicate non fatal errors either to return nil from methods when an error has occurred or to use NSError. A good
example of returning nil is an non successful initialiser i.e. with wrong
parameter.
- (id)initWithValue:(id)value {
if((self = [super init])) {
if (![value class] isKindOfClass: [MyExpectedClass class]) {
self = nil;
} else {
// Initialize instance
}
}
return self;
}
In this scenario a caller of the initialiser will understand that there has been an error, because no instance will have been created.
An NSError object encapsulates richer and more extensible error information than is possible using only an error code or error string. An NSError object encapsulates the following information:
- Error domain (String)
The domain in which the error occurred. This is usually a global variable that can be used to uniquely define the source of the error. Here is a list of predefined error domains:NSPOSIXErrorDomain, NSOSStatusErrorDomain, NSMachErrorDomain, NSCocoarErrorDomain. - Error code (integer)
A code that uniquely defines within a certain error domain what error has occurred. Often, an enum is used to define the set of errors that can occur within a certain error domain. HTTP requests that fail might use the HTTP status code for this value, for example. As listed here error codes are declared and documented in one or more header files for each major domain. - User info (Dictionary):
Extra information about the error, such as a localized description and another error representing the error that caused this error to occur.
The first way in which errors are commonly used in API design is though the use of delegate protocols. When an error occurs, an error object is passed to its delegate through one of the protocol’s methods. For example, NSURLConnection includes the following method as part of its delegate protocol , NSURLConnectionDelegate:
- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
The other common way in which NSError is used is through an out-parameter passed to a method. It looks like this:
- (BOOL)doSomething:(NSError**)error
The error variable bing passed to the method is a pointer to a pointer to an NSError. This enables the method to return an NSError object in addition to its return value.It’s used like this:
NSError *error = nil;
BOOL ret = [object doSomething:&error];
if (error) {
// There was an error handle it
}
Often, methods that return an error like this also return a boolean to indicate success or failure so that you can check the boolean only if you don’t care about the error.
BOOL ret = [object doSomething:nil];
if (ret) {
// There was an error handle it
}
In reality, when using ARC, the compiler translates the NSError** in the method signature to NSError*__autoreleasing*; this means that the object pointed to will be autoreleased at the end of the method.
