• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: NSError: why returned directly?
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

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
  • Follow-Ups:
    • Re: NSError: why returned directly?
      • From: Ondra Cada <email@hidden>
References: 
 >NSError: why returned directly? (From: Ondra Cada <email@hidden>)

  • Prev by Date: Re: NSError: why returned directly?
  • Next by Date: Re: NSError: why returned directly?
  • Previous by thread: Re: NSError: why returned directly?
  • Next by thread: Re: NSError: why returned directly?
  • Index(es):
    • Date
    • Thread