You are on page 1of 24

Objective C Error Handling

Asfar K
Different Methods
Error Handling using NSError
Expected Error
Network failure, Disk Full, Input validation, etc
Exception Handling
used solely for programmer errors
out-of-bounds array access or invalid method
arguments.
These are the problems that you should find and
fix during testing before you ship your app.
Use NSError for Most Errors
Errors are an unavoidable part of any apps lifecycle.
For example, if you need to request data from a
remote web service, there are a variety of potential
problems that may arise, including:
No network connectivity
The remote web service may be inaccessible
The remote web service may not be able to serve the
information you request
The data you receive may not match what you were
expecting
You must plan for errors and know how to deal with
them to give the best possible user experience.
NSError
An NSError object contains
a numeric error code,
domain and description,
user info dictionary.
Rather than making the requirement that every
possible error have a unique numeric code, Cocoa and
Cocoa Touch errors are divided into domains.
For example, if an error occurs in an NSURLConnection,,
the connection:didFailWithError: method will provide an
error from NSURLErrorDomain.
The error object also includes a localized description,
such as A server with the specified hostname could
not be found.
Delegate Methods Alert You to Errors
If youre implementing a delegate object for use with a framework class that
performs a certain task, like downloading information from a remote web service,
youll typically find that you need to implement at least one error-related method.
Example: the NSURLConnectionDelegate protocol includes a
connection:didFailWithError: method:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError
*)error;

If an error occurs, this delegate method will be called to provide you with an
NSError object to describe the problem.
An NSError object contains a numeric error code, domain and description, as well
as other relevant information packaged in a user info dictionary.
Rather than making the requirement that every possible error have a unique
numeric code, Cocoa and Cocoa Touch errors are divided into domains. If an error
occurs in an NSURLConnection, for example, the connection:didFailWithError:
method above will provide an error from NSURLErrorDomain.
The error object also includes a localized description, such as A server with the
specified hostname could not be found.
Some Methods Pass Errors by
Reference
As an example, in NSData method writeToURL:options:error: The last parameter to this
method is a reference to an NSError pointer:
- (BOOL)writeToURL:(NSURL *)aURL options:(NSDataWritingOptions)mask
error:(NSError **)errorPtr;
Before you call this method, youll need to create a suitable pointer so that you can pass its
address:
NSError *anyError;
BOOL success = [receivedData writeToURL:someLocalFileURL options:0 error:&anyError];
if (!success) {
NSLog(@"Write failed with error: %@", anyError);
// present error to user
}
If an error occurs, the writeToURL:... method will return NO, and update your anyError
pointer to point to an error object describing the problem.
When dealing with errors passed by reference, its important to test the return value of the
method to see whether an error occurred, as shown above. Dont just test to see whether
the error pointer was set to point to an error.
Recover if Possible or Display the Error
to the User
The best user experience is for your app to
recover transparently from an error.
If youre making a remote web request, you might try
making the request again with a different server.
Request additional information from the user such as
valid username or password credentials before trying
again.
If its not possible to recover from an error, you
should alert the user.
Create and configure a UIAlertView to display the
error.
Localized Error Information
NSError objects contain error information that programs can display in an alert
dialog .
This information is usually stored in the user info dictionary as strings in several
categories:
description, failure reason, recovery suggestion, recovery options.
When you create an NSError object, you should insert localized strings into the
dictionary.
Dont expect the user info dictionary of every error object to contain localized
strings. A subclass of NSError, for example, could override localizedDescription to
compose these strings on-the-fly from the error domain, code, and context instead
of storing them.
To get localized information associated with an NSError
Send objectForKey: to the user info dictionary, specifying the appropriate key.
Or you can send an equivalent message to the NSError object.
However, you should send the message rather than use the dictionary key to
access a localized string.
To get localized string
Error description (User info key: NSLocalizedDescriptionKey , Method:
localizedDescription )
The main description of the error, appearing in a larger, bold type face.
It often includes the failure reason. If no error description is present in
the user info dictionary, NSError either constructs one from the error
domain and code or (when the domain is well-known , such as
NSCocoaErrorDomain), attempts to fetch a suitable string from a
function or method within that domain. .
Failure reason (User info key: NSLocalizedFailureReasonErrorKey Method:
localizedFailureReason)
A brief sentence that explains the reason why the error occurred. It is
typically part of the error description. The failure reason is for clients
that only want to display the reason for the failure.
Note: An error object has an error description of File could not be saved
because the disk is full. The accompanying failure reason is The disk is
full.
To get localized string
Recovery suggestion (User info key: NSLocalizedRecoverySuggestionErrorKey
Method: localizedRecoverySuggestion)
A secondary sentence that ideally tells users what they can do to recover
from the error. It appears beneath the error description in a lighter type
face. If the recovery suggestion refers to the buttons of the error alert, it
should use the same titles as specified for recovery options
(NSLocalizedRecoveryOptionsErrorKey). You may use this string as a purely
informative message, supplementing the error description and failure
reason.
Recovery options (User info key: NSLocalizedRecoveryOptionsErrorKey
Method: localizedRecoveryOptions )
An array of titles (as strings) for the buttons of the error alert. By default,
alert sheets and dialogs for error messages have only the OK button for
dismissing the alert. The first string in the array is the title of the rightmost
button, the next string is the title of the button just to the left of the first,
and so on. Note that if a recovery attempter is specified for the error
object, the recovery-options array should contain more than one string.
The recovery attempter accesses the recovery options in order to
interpret user choices.
Example
Generating Your Own Errors
Define your own error domain of the form:
com.companyName.appOrFrameworkName.ErrorDo
main
Choose a unique error code for each error that may
occur in your domain, along with a suitable
description, which is stored in the user info
dictionary for the error.
NSString *domain = @"com.MyCompany.MyApplication.ErrorDomain";
NSString *desc = NSLocalizedString(@"Unable to", @"");
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : desc };
NSError *error = [NSError errorWithDomain:domain
code:-101 userInfo:userInfo];
Exceptions
Similar syntax to Java or C++.
Represented by instances of the NSException class
If you need to write code that might cause an exception to be thrown, you
can enclose that code inside a try-catch block:
@try {
// do something that might throw an exception
}
@catch (NSException *exception) {
// deal with the exception
}
@finally {
// optional block of clean-up code
// executed whether or not an exception occurred
}
Exception
If an exception is thrown by the code inside the @try block, it will
be caught by the @catch block so that you can deal with it.
If an exception is thrown and not caught, the default uncaught
exception handler logs a message to the console and terminates
the application.
You should not use a try-catch block in place of standard
programming checks for Objective-C methods.
In the case of an NSArray, you should always check the arrays count to
determine the number of items before trying to access an object at a
given index.
The objectAtIndex: method throws an exception if you make an out-of-
bounds request so that you can find the bug in your code early in the
development cycle
You should avoid throwing exceptions in an app that you ship to users.
The program catching an exception should quit soon afterwards
Exception Handling
Most exceptions are thrown in exceptional circumstances.
(Some exceptions aren't, like accessing an object out of
NSArray bounds.)
.NET/java encourages local exception handling, but Cocoa
encourage large-scope exception handling.
The reason you have local exception handling in .NET is
that you expect some part to fail in an expected way (like a
network error when downloading something). In Cocoa,
this is handled by using methods that return NSErrors
instead. It's the same thing, only more visible in the
method signature.
A good rule of thumb is that Cocoa only throws an
exception in situations where it's unclear how you would
even recover.
NSException
The attributes of an NSException object are:
A name a short string that is used to uniquely
identify the exception.
A reason a longer string that contains a
human-readable reason for the exception.
An optional dictionary (userInfo) used to supply
application-specific data to the exception handler.
For example, if the return value of a method causes an
exception to be raised, you could pass the return value
to the exception handler through userInfo.
sequence of @catch blocks
Each block handles an exception object of a different type. You should order this
sequence of @catch blocks from the most-specific to the least-specific type of
exception object (the least specific type being id).
@try {
// code that throws an exception
}
@catch (CustomException *ce) { // most specific type
// handle exception ce
}
@catch (NSException *ne) { // less specific type
// do whatever recovery is necessary at his level
}
@catch (id ue) { // least specific type
// code that handles this exception
}
Throwing Exceptions
Process of propagating an exception is referred to as
"throwing an exception (or "raising an exception" ).
You throw (or raise) an exception by instantiating an
NSException object and then doing one of two things with it:
Using it as the argument of a @throw compiler directive
@throw myException;
Sending it a raise message
[myException raise];
An important difference between @throw and raise is that the
latter can be sent only to an NSException object whereas @throw
can take other types of objects as its argument (such as string
objects).
Cocoa applications should @throw only NSException objects.
Uncaught Exceptions
If an exception is not caught, it is intercepted by a
function called the uncaught exception handler.
The uncaught exception handler always causes the
program to exit but may perform some task before this
happens.
The default uncaught exception handler logs a message
to the console before it exits the program.
You can set a custom function as the uncaught
exception handler using the
NSSetUncaughtExceptionHandler function;
Get the current uncaught exception handler with the
NSGetUncaughtExceptionHandler function.
Predefined Exceptions
Cocoa predefines a number of generic exception. You can also create and use
custom exception names.
The generic exception names are string constants defined in NSException.h
NSGenericException
NSRangeException
NSInvalidArgumentException
NSInternalInconsistencyException
NSObjectInaccessibleException
NSObjectNotAvailableException
NSDestinationInvalidException
NSPortTimeoutException
NSInvalidSendPortException
NSInvalidReceivePortException
NSPortSendException
NSPortReceiveException
Predefined Exceptions
In addition to the generic exception names, some
subsystems of Cocoa define their own exception
names, such as NSInconsistentArchiveException and
NSFileHandleOperationException.
These are documented in Foundation Constants
Reference .
You can identify caught exceptions in your exception
handler by comparing the exception's name with these
predefined names.
Note that all predefined exceptions begin with the
prefix "NS", so you should avoid using the same prefix
when creating new exception names.
Signal handling
A crash (an unexpected termination) is the result of an unhandled
signal sent to your application.
An unhandled signal can come from three places:
the kernel
other processes
the application itself.
The two most common signals that cause crashes are:
EXC_BAD_ACCESS is a Mach exception sent by the kernel to your
application when you try to access memory that is not mapped for
your application.
If not handled at the Mach level, it will be translated into a SIGBUS or
SIGSEGV BSD signal.
SIGABRT is a BSD signal sent by an application to itself when an
NSException or obj_exception_throw is not caught.
Catching uncaught exceptions/signal
The correct way to handle an uncaught exception is to
fix the cause in your code.
Of course, programs do sometimes get released with
bugs that may lead to a crash.
You need to get more information back from your
testers when you know that there are bugs in your
program.
In these cases, there are two ways to catch otherwise
uncaught conditions that will lead to a crash:
Use the function NSUncaughtExceptionHandler to install a
handler for uncaught Objective-C exceptions.
Use the signal function to install handlers for BSD signals.
Objective-C exception handler and
handlers for common signals
void InstallUncaughtExceptionHandler()
{
NSSetUncaughtExceptionHandler(&HandleException);
signal(SIGABRT, SignalHandler);
signal(SIGILL, SignalHandler);
signal(SIGSEGV, SignalHandler);
signal(SIGFPE, SignalHandler);
signal(SIGBUS, SignalHandler);
signal(SIGPIPE, SignalHandler);
}
Responding to the exceptions and signals can then happen in the
implementation of the HandleException and SignalHandler.
In most cases, both call through to the same internal
implementation so that the same work can be done in either case.