Check out our Bootcamp Schedule View Schedule

An NSError Error

Mark Dalrymple

We have a lot of very convenient, very powerful methods at our disposal such as [NSData dataWithContentsOfFile:]. This method goes to the file system, opens the file, reads in all the bytes, closes the file, packs the bytes into an NSData, and returns it back to us. It replaces a loop and several other lines of code into one convenient package. If it can’t do the work, it returns nil. That’s pretty simple.

There’s just one problem. It’s great that you’re told that things didn’t work, but you have no idea why it failed. File doesn’t exist? Permissions problem? File too big to fit into memory? You don’t know, so you might write code like this:

NSData *data = [NSData dataWithContentsOfFile: @"/usr/share/dict/words"];
if (data == nil) {
    // uh, tell the user something.  Wish we knew more details.
}

An alternative is to write your own open / read-loop / pack / close code so you know exactly what went wrong, so you can tell the user.

With Mac OS X 10.5 Apple started introducing methods that return error information via a pointer you pass in:

NSError *error;
NSData *data = [NSData dataWithContentsOfFile: @"/usr/share/dict/words"
                       options: 0
                       error: &error;];
if (data == nil) {
    // Tell the user exactly what happened by digging 
    // into the error object.
}

You can think of the error details as out-of-band data, a secondary information stream coming from the method. “Here are the bytes you asked for. Oh, and if something went wrong, you can look over here for more information.” You give a pointer to an NSError pointer (which ends up being an NSError **) which gets filled in with an (autoreleased) NSError if something goes wrong.

I do a lot of code review for friends and coworkers, and I’ve seen a common mistake, even with experienced developers: using the value of the error pointer, rather than the return value of the method, to decide whether the method succeeded or failed

This is bad code:

NSError *error;
NSData *data = [NSData dataWithContentsOfFile: @"/usr/share/dict/words"
                       options: 0
                       error: &error;];
if (error != nil) {
    // Please don't use the error pointer to decide method success or failure.
    // Compare |data| to nil first
}

You should always look at the return code of the method. In the case of dataWithContentsOfFile:..., you should test to see if data is nil, and only if it is nil would you use the error pointer for more elaboration. On success, the method being called is under no obligation to fill in error with a sane value. It could fill it with an insane value. It could even assign a temporary object to it and not reset it to NULL. So you cannot trust the value of the returned error pointer unless the method told you to.

Apple’s rules are pretty simple: the error pointer is hands-off unless the method indicates a failure. Trying to do otherwise could invite odd behavior, crashes, or at the very least your coworkers saying “neener neener neener” when they see the code.

Interested in error handling? Advanced Mac OS X Programming: The Big Nerd Ranch Guide devotes an entire chapter to the topic.

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project