Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

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.

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.

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

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.

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