Why does NSError need double indirection? (pointer to a pointer)

Viewed 11112

This concept seems to trouble me. Why does an NSError object need its pointer passed to a method that is modifying the object? For instance, wouldn't just passing a reference to the error do the same thing?

NSError *anError;
[myObjc doStuff:withAnotherObj error:error];

and then in doStuff:

 - (void)doStuff:(id)withAnotherObjc error:(NSError *)error 
 {
    // something went bad!
    [error doSomethingToTheObject];
 }

Why doesn't the above work like most other object messaging patterns work? Why must instead we use error:(NSError **)error?

5 Answers

Quite simply:

if you pass a pointer to an object to your function, the function can only modify what the pointer is pointing to.

if you pass a pointer to a pointer to an object then the function can modify the pointer to point to another object.

In the case of NSError, the function might want to create a new NSError object and pass you back a pointer to that NSError object. Thus, you need double indirection so that the pointer can be modified.

The NSError** pattern is used when a method normally returns some value but instead may need to return an error object (of type NSError*) if it fails. In Objective-C a method can only return one type of object, but this is a case where you want to return two. In C-like languages when you need to return an extra value you ask for a pointer to a value of that type, so to return an NSError* you need an NSError** parameter. A more realistic example would be this:

// The method should return something, because otherwise it could just return
// NSError* directly and the error argument wouldn't be necessary
- (NSArray *)doStuffWithObject:(id)obj error:(NSError **)error
{
  NSArray *result = ...;  // Do some work that might fail
  if (result != nil) {
    return result;
  } else {
    // Something went bad!
    // The caller might pass NULL for `error` if they don't care about
    // the result, so check for NULL before dereferencing it
    if (error != NULL) {
      *error = [NSError errorWithDomain:...];
    }
    return nil;  // The caller knows to check error if I return nil
  }
}

If you only had an NSError* parameter instead of an NSError** then doStuff would never be able to pass the error object back to its caller.

Alternate statement of what n8gray said:

Because you're not receiving an object to send messages to; you're creating the object and returning it. You generally need the pointer-to-an-NSError *-variable argument because you can only use the return statement on one thing at a time, and you're already using it with NO.

Related