Search

An NSError Error

Mark Dalrymple

3 min read

Mar 21, 2012

An NSError Error

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.

Mark Dalrymple

Author Big Nerd Ranch

MarkD is a long-time Unix and Mac developer, having worked at AOL, Google, and several start-ups over the years.  He’s the author of Advanced Mac OS X Programming: The Big Nerd Ranch Guide, over 100 blog posts for Big Nerd Ranch, and an occasional speaker at conferences. Believing in the power of community, he’s a co-founder of CocoaHeads, an international Mac and iPhone meetup, and runs the Pittsburgh PA chapter. In his spare time, he plays orchestral and swing band music.

Speak with a Nerd

Schedule a call today! Our team of Nerds are ready to help

Let's Talk

Related Posts

We are ready to discuss your needs.

Not applicable? Click here to schedule a call.

Stay in Touch WITH Big Nerd Ranch News