Question

why is "error:&error" used here (objective-c)

NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];

wouldn't an object in objective-c be effectively pass-by-reference anyway?

Was it helpful?

Solution

The argument type for error: is NSError** (i.e. a pointer to a pointer to an object). This permits the moc object to allocate and initialize a new NSError object as required. It is a common pattern, especially in Cocoa.

The NSError documentation gives some indication of the motivation for this approach:

Applications may choose to create subclasses of NSError to provide better localized error strings by overriding localizedDescription.

Passing in an NSError** argument allows that method to return any subclass of NSError that makes sense. If you passed in NSError*, you would have to supply an existing NSError object, and there would be no way for the method to return a different object from the one you passed in.

To be clear, the method could look something like this:

- (NSArray*)executeFetchRequest:(Request *)request error:(NSError**)error {
    ...
    if ((error != NULL) && (some_error_condition)) {
        *error = [[[SomeNSErrorSubclass alloc] init...] autorelease];
        return nil;
    }
}

Note that this also allows the calling code to ignore errors by simply passing in NULL for the error: parameter, as follows:

NSArray *array = [moc executeFetchRequest:request error:NULL];

Update: (in response to questions):

There are two reasons why the argument type has to be NSError** instead of NSError*: 1. variable scoping rules, and 2. NSError instances are imutable.

Reason #1: variable scoping rules

Let's assume that the function declaration were to look like this:

- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error;

And we were to call the function like this:

NSError * error = nil;
[someArray executeFetchRequest:someRequest error:error];
if (error != nil) { /* handle error */ }

When you pass in a variable this way, the function body will not be able to modify the value of that variable (i.e. the function body will not be able to create a new variable to replace the existing one). For example, the following variable assignments will exist only in the local scope of the function. The calling code will still see error == nil.

- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error {
    ...
    error = [[[NSError alloc] init...] autorelease];             // local only
    error = [[[SomeNSErrorSubclass alloc] init...] autorelease]; // local only
}

Reason #2: instances of NSError are immutable

Let's keep the same function declaration, but call the function like this:

NSError * error = [[[NSError alloc] init...] autorelease];
[someArray executeFetchRequest:someRequest error:error];
if (error != nil) { /* handle error */ }

First of all, the variable scoping rules guarantee that error can not be nil, so the if (error != nil) { ... condition will always be true, but even if you wanted to check for specific error information inside the if block, you would be out of luck because instances of NSError are immutable. This means that once they are created, you cannot modify their properties, so the function would not be able to change the domain or userInfo of that NSError instance that you created in the calling code.

- (NSArray*)executeFetchRequest:(Request *)request error:(NSError*)error {
    ...
    error.domain = ...   // not allowed!
    error.userInfo = ... // not allowed!
}

OTHER TIPS

It's effectively another return value. The error is not dominant by convention in Cocoa when there is a return value for the operation. When an error is encountered, it may be returned to you by this out parameter.

In the case of NSError, it works this way because NSError is not a mutable type - its fields are set at initialization and never mutated. Therefore, you cannot pass an NSError as usual and set the error code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top