Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the rationale behind Apple's pattern of checking return value rather than error?

Apple's guide on Using and Creating Error Objects gives the following code example:

NSError *theError;
BOOL success = [myDoc writeToURL:[self docURL] ofType:@"html" error:&theError];

if (success == NO) {
    // Maybe try to determine cause of error and recover first.
    NSAlert *theAlert = [NSAlert alertWithError:theError];
   [theAlert runModal]; // Ignore return value.
}

and accompanies it with the statement:

Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.

I have always been wondering why is this pattern so important? Why should we ALWAYS check the return value? What's wrong if we check whether the error is nil or not?

like image 560
Andrey Chernukha Avatar asked Jan 28 '26 15:01

Andrey Chernukha


2 Answers

This design is not terribly unusual, compare also errno in standard C.

The design has a number of potential advantages:

  • The function does not have to write through a pointer on success. This does not only make the implementation of such functions easier and less error prone, it can also be a small performance advantage (e.g. this prevent CPU caches from being invalidated if the function succeeds).

  • If we always check that the function failed before accessing an error, we can use that same error pointer for multiple functions. Otherwise, we might get a previous failure rather than the failure of the most recent function.

  • This makes validation code easier to write. E.g. a function could set the error by default. If all validations pass, the function can simply return success instead of having to reset the error variable.

  • A function can use the same error pointer when calling other functions, but a failure of these helpers doesn't necessarily imply a failure of the top function.

In your specific case, the variable NSError *theError; has not been initialized. Accessing that variable without assigning to it first would invoke undefined behaviour. The documentation only guarantees that the variable will be set in case of an error.

like image 121
amon Avatar answered Jan 30 '26 05:01

amon


Imagine you implement a method in terms of a few other methods:

-(BOOL)sendCachedRequestReturningError: (NSError**)err {
    BOOL success = [self readCachedRequestReturningError:err];
    if (!success && (*err).domain == MYFileDomain && (*err).errorCode == MYFileNotFoundCode) {
        success = [self sendUncachedRequestReturningError:err];
    }

    return success;
}

Now there are 4 code paths here:

  1. There is a cached request. We'll just return success == YES and all is good.
  2. A non-recoverable error occurs trying to read from the cache. readCachedRequestReturningError: will set err and set success == NO and the caller will call presentError: or whatever
  3. An error occurs trying to do the network request. Same as #2, err is set, and success == NO.
  4. There is no cache, but we can make a network request. readCachedRequestReturningError: will set err to a valid NSError{MYFileDomain, MYFileNotFoundCode}, but then sendUncachedRequestReturningError: will succeed and set success == YES, and not touch err at all, leaving the previous error in it. If you now check err instead of checking the return value, you will think there was an error when all went well.

Note: The code above is grossly simplified, because we only care about errors. Of course in a real program, the methods would probably have another return parameter for the actual reply from the request, or would return the reply or nil instead of a success BOOL. It would also probably check whether err is NULL.

like image 36
uliwitness Avatar answered Jan 30 '26 03:01

uliwitness



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!