Re: NSError: why returned directly?
Re: NSError: why returned directly?
- Subject: Re: NSError: why returned directly?
- From: Ali Ozer <email@hidden>
- Date: Wed, 26 Apr 2006 08:56:00 -0700
Good points.
Important reasons for not using exceptions to signal expected errors:
- Lack of a formalism to indicate whether something returns an
error. This means you're never sure whether a method will return an
error, or whether if might return an error in the future. You can
still go ahead and treat it as if it will return an error (by
wrapping it with an exception handler), but you don't know from the
method signature, so it requires reading the documentation. There is
also no change in the method signature if some day the method does
start raising these exceptions. Java has a formalism for this, but
even there the question is what happens when a method does start
returning errors --- since that is now a potentially incompatible
change.
- Historically the way exceptions have been handled. There is not too
much exception-clean code out there, so exceptions are likely to lead
to leaks and other loose ends. In fact, our general recommendation is
if a programmer-error exception is encountered by the end-user, that
they go ahead and try to save and quit as soon as possible. However,
we want expected runtime errors to be handled cleanly, and all loose
ends tied up, since app execution continues. So we stick to
traditional return value paradigm.
- Our guidelines state that NSError should be used for problems that
need to be reported to the user. And we would like to see that done
very well --- that the user gets a clear statement of what went
wrong, and why, with a possible recovery suggestion. And as you know
Cocoa has APIs to help automate the presentation as much as possible.
Given this, rather than sprinkling errors across a vast number of
methods, we would rather put them on select, well thought out methods
whose results are important to bubble up to the user. So hopefully
we're not attaching NSError arguments to every single method, but
just those cases where they're useful and needed. Therefore the
burden shouldn't be huge. And again, having the formal declaration is
important.
- We also have the guideline of passing NULL in as the (NSError **)
argument, which both makes it easier for the caller if for some
reason they don't need to know error details, and more efficient for
the callee if they take care not to create an NSError in that case.
The reason I mention this is that even though there is an explicit
error argument, ignoring the error is pretty easy, if desired.
Ignoring an exception is also easy, but your code ends up somewhere
totally different.
Having said all this, additional patterns are possible - for
instance, some subsystems might very well have an
- (NSError *)lastError;
type approach which the developer can call on a method which returns
an error. This would enable returning NSErrors for an existing set
of APIs without much disruption. Clearly this is best done if the
receiving object is used per-thread and the error applies to the
thread it occurred in.
Ali
On Apr 25, 2006, at 4:49 PM, Ondra Cada wrote:
Hello,
if listmom is not offended by a, say, rather academical question...
does anyone happen to know what is the rationale behind the
particular way NSError is returned? It is well possible I am
overlooking something pretty obvious, but it seems to me that in a
sense, we are returning to the old bad days of error codes returned
and explicitly tested anywhere, making the code unnecessarily
complex and error-prone.
The trick is, perhaps it is just my fault, or I do something far
wrong, but a *very vast majority* of my usages of NSError tends to
look like this:
-(id)someMethodWithError:(NSError**)error { // pattern X
...
if (![foo anotherMethodWithError:error]) return nil;
...
if (![bar justAnotherMethodWithError:error]) return nil;
...
// and so forth
}
Only a small minority of my methods actually does something with
the error, be it extending, translation of attributes, or whatever.
Even GUI-level often error is just re-directed in the above way, to
be presented the standard way into the responder chain at the top
code level.
Is it just me, or do perhaps others found similar results? I find
the "pattern X" above pretty error-prone and conceptually unclean,
and I feel the more methods in future supports NSError, the more
PITA it will beome. But it might be just my fault or my
misunderstanding of something very basic?
Since the above, I would prefer myself a different, exception-like
implementation. Actually, with the new exception support which can
throw anything, it could have been used directly: instead of
setting NSError into the reference argument and returning a nil (or
NO or whatever), the same NSError would be thrown. Instead of
checking for the return value, NSError would be catched.
Thus, the code--in my personal and possibly faulty opinion--would
become considerably cleaner and less error-prone:
-(id)someMethodWhichGeneratesError:(NSError**)error { // current style
...
if (something) {
if (error) *error=[NSError errorWithDomain:...];
return nil;
}
...
}
would turn to
-(id)someMethodWhichGeneratesError { // proposed style
...
if (something) @throw [NSError errorWithDomain:...];
...
}
Similarly,
-(IBAction)someDocumentMethodWhichUsesError { // current style
...
if (![foo someMethodWhichGeneratesError:error]) {
[self presentError:error modalForWindow:...];
return;
}
...
if (![bar anotherMethodWhichGeneratesError:error]) {
[self presentError:error modalForWindow:...];
return;
}
...
// and so forth
}
would turn to
-(IBAction)someDocumentMethodWhichUsesError { // proposed style
...
@try {
...
[foo someMethodWhichGeneratesError];
...
[bar anotherMethodWhichGeneratesError];
...
// and so forth
} @catch (NSError *error) {
[self presentError:error modalForWindow:...];
}
}
Finally, and best of all, intermediate methods (like my "pattern X"
of above), would not have to check for errors at all, unless they
want to change it. Another advantage would be that there could be a
top-level handler somewhere around the event loop level, which
would harness any action in
@try { ... application code here ... } @catch (NSError *error)
{ [NSApp presentError:error]; }
What am I overlooking, and why something conceptually like this was
not used?
Thanks,
---
Ondra Čada
OCSoftware: email@hidden http://www.ocs.cz
private email@hidden http://www.ocs.cz/oc
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Cocoa-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden