Re: Initializing NSError **, again Re: CoreData Migration Problems
Re: Initializing NSError **, again Re: CoreData Migration Problems
- Subject: Re: Initializing NSError **, again Re: CoreData Migration Problems
- From: Quincey Morris <email@hidden>
- Date: Wed, 9 Feb 2011 16:31:39 -0800
On Feb 9, 2011, at 12:46, Greg Parker wrote:
> Initializing the error to nil is helpful to protect against one nasty hole in the pattern. Consider this code:
>
> NSError *error;
> id result = [receiver doSomethingWithError:&error];
> if (!result) NSLog(@"error %@", error);
>
> On failure, -doSomethingWithError: returns nil and sets error. But if receiver is nil, -doSomethingWithError: returns nil and does not set error. In that case, the error-handling code may crash after using the uninitialized error value. If you set error=nil in advance, you can do something correct when receiver is nil.
After reading this, I was going to break my resolution to stay out of the thread, to say, "OK, Greg Parker wins with an unanswerable argument." Except that after a little thought I decided it wasn't unanswerable. :)
The problem with your initialization pattern**:
> NSError *error = nil;
> id result = [receiver doSomethingWithError:&error];
> if (!result) NSLog(@"error %@", error);
as a defensive technique is that it breaks down in the following variant of the scenario:
> NSError *error = nil;
> id result = [receiver1 doSomethingWithError:&error];
> if (!result) NSLog(@"error %@", error);
> result = [receiver2 doSomethingWithError:&error];
> if (!result) NSLog(@"error %@", error);
Now, I'll be the first to admit that my second example is bait-and-switch. We weren't talking about multiple method invocations, but in this particular scenario the second invocation could result in mis-reporting an error actually detected earlier.
What's *really* going on here is a different problem -- it's related to the validity of return values from messages to a nil receiver, a topic you've weighed in on the past. Just as a struct return value from a message to a nil object isn't valid, an error "return" value from a message to a nil object isn't valid either.
Therefore, I'd suggest, the morally correct version of my second example is not this:
> NSError *error;
> error = nil;
> id result = [receiver1 doSomethingWithError:&error];
> if (!result) NSLog(@"error %@", error);
> error = nil;
> result = [receiver2 doSomethingWithError:&error];
> if (!result) NSLog(@"error %@", error);
but this:
> NSError *error;
> id result = [receiver1 doSomethingWithError:&error];
> if (receiver1 && !result) NSLog(@"error %@", error);
> result = [receiver2 doSomethingWithError:&error];
> if (receiver2 && !result) NSLog(@"error %@", error);
which is closer to what you'd have to do if 'result' was a NSPoint, say, instead of an id -- and is clearer about what subtlety is being dealt with.
So unless you have another unanswerable argument up your sleeve, I'll stick with my earlier claim that setting error to nil actively leads the unwary developer astray to some degree.
** Actually, there might be a problem even with the simple version of your initialization pattern: there might still need to be additional code later to deal with the nil error object after the error-handling code path was taken. Probably, in most cases, there won't be a transparent way of handling the nil receiver.
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden