views:

537

answers:

6

I am working on porting a Java codebase to Cocoa/Objective-C for use on desktop Mac OS X. The Java code has lots and lots of methods with checked exceptions like:

double asNumber() throws FooException {
    ...
}

What's the best way to represent these in Objective-C? Exceptions or error out-parameters?

- (CGFloat)asNumber { 
    ... // possibly [FooException raise:format:];
}

or

- (CGFloat)asNumberError:(NSError **)outError {
    ...
}

I have the sense that out-errors are generally the better solution for Objective-C, but as you can see... a lot of methods like the one above will be quite awkward-looking. And again, there are lots of these.

Of course keep in mind that since these are checked exceptions in Java, I will need to add either @try blocks or if (*outError) {...} checks wherever these methods are called (lots of places).

I recall hearing that while entering @try blocks was once expensive in Objective-C, it is cheap in 64-bit or SL or some other new env (don't recall exactly). I am not at all concerned about backwards compatibility, so i am definitely willing to design only for the new hotness.

A: 

Exceptions are probably the best approach, as the 64-bit Obj-C ABI (runtime) uses zero cost exceptions, so you get cleaner code at no real cost. Of course in 32-bit the old setjmp/longjmp exceptions are still in use and they don't interact with C++ so if that's a goal then you have a problem.

olliej
thx for the great feedback oliver. and for the record, no. i will not be mixing this codebase with C++ in any way.
Todd Ditchendorf
-1 Sorry, but this is TERRIBLE advice for Objective-C. Not all exceptions are "zero cost", only those that never actually occur are (much like uncontended locks when programming with concurrency). The moment you raise ("throw") an exception, you pay the price. Claiming it results in "cleaner code" is a red herring, because although the library code can just raise an exception, every single client must deal with the exception using @try and @catch. The "Cocoa way" is to use errors and return codes for such situations. If you want the port to be good Objective-C code, DON'T USE EXCEPTIONS.
Quinn Taylor
@Quinn: "Zero cost" exceptions are a well understood term that specifically means zero cost if the exception is not thrown. The 32-bit Obj-C ABI uses setjmp and longjmp to implement exceptions, meaning that @try/@catch have a large cost whether the exception is thrown or not.
olliej
I understand that, but to Objective-C newbies, it makes it sound like throwing an exception is "zero cost", which of course it is not. (You could argue that using NSError is "even more zero cost" than exceptions.) Aside from that minor issue, the bigger beef is that exceptions are not the right approach for this particular situation. It may be correct in Java or C++, but not in Objective-C. I apologize if it seemed like a personal attack, I just wanted to make it vehemently clear to readers of the question that they should strongly consider other approaches.
Quinn Taylor
+3  A: 

You're correct that "out errors are generally the better solution for ObjC". Very rarely will you find an API in Cocoa that throws an exception (unless you haven't satisfied the preconditions for the API, but in that case, behavior is undefined by default).

If you expect this code to live beyond you and be adopted by other Cocoa developers, I would recommend using out errors. I work on code that was built by people unfamiliar with Cocoa and who used exceptions liberally, and they're a royal pain to work around.

Dave DeLong
+9  A: 

You should definitely avoid exceptions for things like parsing numbers from strings. In Objective-C, exceptions represent programmer error, not user input error, or even unavailable files. (Part of the reason is that exception handling is always more costly and complex than more "conventional" error handling. Regardless of the fact that entering @try blocks is "zero cost" in 64-bit, it's still slow whenever an exception is actually raised.) Of course, you're allowed to use exceptions as you like, but it's not the Cocoa way, and you'll find yourself at odds with other Objective-C code. People who use your code will be incredibly annoyed that you throw exceptions in cases that should just result in an error.

From Apple's own docs:

"In many environments, use of exceptions is fairly commonplace. For example, you might throw an exception to signal that a routine could not execute normally—such as when a file is missing or data could not be parsed correctly. Exceptions are resource-intensive in Objective-C. You should not use exceptions for general flow-control, or simply to signify errors. Instead you should use the return value of a method or function to indicate that an error has occurred, and provide information about the problem in an error object."

Look at how built-in Cocoa classes handle errors like this. For example, NSString has methods like -floatValue that return 0 if it fails. A better solution for your particular situation might be how NSScanner does it, such as in -scanFloat: — accept a pointer to the field where the result should be stored, and return YES or NO based on whether the parse was successful.

Aside from Obejctive-C convention and best practices, NSError is much more robust and flexibly than NSException, and allows the caller to effectively ignore the problem if they want to. I suggest reading through the Error Handling Programming Guide For Cocoa. Note: If you accept an NSError** param, I strongly suggest you also design to allow the client to pass NULL if they don't want to receive any error information. Every Cocoa class I'm aware of does this for errors, including NSString.

Although the ported code may end up looking totally different from the Java code, recognize that it will be used by Objective-C code, not the same clients of the Java equivalent. Definitely match the idioms of the language. The port will not be a mirror image of the Java code, but it will be much more correct (for Objective-C) as a result.

Quinn Taylor
thx for your feedback Quinn
Todd Ditchendorf
i especially like the `NSScanner` advice... that will help clean these method names up a little and make them more natural in ObjC.
Todd Ditchendorf
+5  A: 

In Cocoa, exceptions are really only supposed to be used for "programming errors;" the philosophy is to let the app catch them, give the user the option to save what they're doing, and quit. For one thing, not all frameworks or code paths may be 100% exception-safe, so this can be the only safe course of action. For errors that can be anticipated and recovered from, you should use NSError, typically via an out-parameter.

Chris Hanson
So Objective-C exceptions are similar to how Error is defined in Java: you're not expected to handle them, but rather shut down gracefully.
Joachim Sauer
+2  A: 

I'm a big fan of the out error approach that Objective-C uses. You HAVE to handle exceptions, but you can choose to ignore out errors if you want to. It all fits with the Objective-C attitude that "the programmer knows what they're doing." It also makes Objective-C a very clean-looking language, because your code isn't cluttered with try-catch blocks.

That said - you might want to consider: Are there any scenarios where exceptions are ignored? Are the exceptions you throw truly critical? Do you find yourself writing simple catch blocks that clean up variables and continue on? I'd lean toward out errors because I like the syntax and Objective-C reserves exceptions for only the most critical errors.

Ben Gotow
hrm, well in the Java source, these are *checked* exceptions, so with the current design, yes, they absolutely must be handled. as to wether they are truly critical i suppose is a tougher question. Maybe they aren't truly critical, and the original author was just taking advantage of a nice feature of the original language (Java).I will say.... i have the highest respect for the original author. he definitely knows his stuff (in Java at least).
Todd Ditchendorf
+1 To be sure, no disrespect is intended to the original author. When I write in Java, I follow conventions (both of the language and my team) and often throw checked exceptions in similar cases. It's more a consideration of context and what fits best with a given context. It's also nice to let the client decide when it's truly critical to handle an error, rather than always having to defend against a NumberFormatException, NullPointerException, etc
Quinn Taylor
+2  A: 

Its looks like these checked exceptions more cleanly map to out errors. Exceptions could still be used, but should be reserved for exceptional circumstances.

Gabe