objective-c - signal - objective c try catch

Objective-C Exceptions (5)

I think your trainer is correct. You can make all kinds of arguments for and against exceptions but the bottom line is if you want your code to "smell" right to an experienced Cocoa developer, you'll implement your application using the same design patterns that Apple use in their code and in their frameworks. That means nils and NSErrors rather than exceptions.

The advantages of not using exceptions are therefore mainly around consistency and familiarity.

An other thing to consider is that a nil in Objective C is usually fairly "safe." That is, you can send any message to a nil and your application won't crash.

(I should also point out that Apple do use exceptions in some places, usually where you're using an API incorrectly.)

I have just completed an iPhone app programming course. As part of the course, I saw

  • Objective-C provides exception handling using the @try directive
  • The system library does not use exception handling, preferring to return nil

I asked if I should use exception handling for new code I wrote (e.g. if I write both the front-end and logic code, so the communication between them is in my hands) but I was told no, one should not use exceptions for new code. (But he failed to elaborate, then the class moved on, I thought perhaps the reason would become clear later..)

Surely exceptions are superior to return nil? You can catch particular types, you are not tempted to ignore them by ignoring the return type of a function which normally succeeds, you have text messages which can be logged, they allow your code to focus on the normal case and thus be more readable. Why to use exceptions.

So what do you think? Was my trainer right not to use Objective-C exceptions? If so, why?

I think, and others will correct me if I am wrong, that Exceptions should be used to catch programmer errors, while the NSError type error handling should be used for exceptional conditions that occur while the program is running.

And as for returning nil, that isn't all - functions that might have problems don't just return a nil, they also can (and should) provide further information using the NSError object that is passed in as a parameter.

See also

It is unsafe to throw exceptions in circumstances where resources are not automatically managed. This is the case of the Cocoa framework (and neighbor frameworks), as they use manual reference counting.

If you throw an exception, any release call you skip over by unwinding the stack will result in a leak. This should limit you tothrowing only if you're certain that you're not going to recover since all resources are returned to the OS when a process quits.

Unfortunately, NSRunLoops tend to catch all exceptions that propagate to them, so if you throw during an event, you'll resume to the next event. This is, obviously, very bad. Therefore, it's better that you simply don't throw.

This problem is diminished if you use garbage-collected Objective-C, as any resource represented by an Objective-C object will be properly released. However, C resources (such as file descriptors or malloc-allocated memory) that are not wrapped in an Objective-C object will still leak.

So, all in all, don't throw.

The Cocoa API has several workarounds to this, as you mentioned. Returning nil and the NSError** pattern are two of them.

Clarifications for ARC

ARC users can choose to enable or disable full exception safety. When exception safety is enabled, ARC will generate code to release strong references when their scope is killed, making it safe to use exception in your code. ARC will not patch external libraries to enable exception support in them, so you should be careful where you throw (and especially where you catch), even with exception support enabled in your program.

ARC exception support can be enabled with -fobjc-arc-exceptions or disabled with -fno-objc-arc-exceptions. By default, it is disabled in Objective-C but enabled in Objective-C++.

Full exception safety in Objective-C is disabled by default because the Clang authors assume that Objective-C programs will not recover from an exception anyways, and because there is a large code size cost and a small performance penalty associated to that cleanup. In Objective-C++, on the other hand, C++ already introduces a lot of cleanup code, and people are much more likely to actually need exception-safety.

This is all from the ARC specification on the LLVM website.

Personally, I see no reason not to use exceptions in your own code. The exception pattern is cleaner for handling errors than

  • always having a return value,
  • making sure that one of the possible return values really means "error"
  • passing an extra parameter which is a reference to an NSError*
  • sprinkling your code with clean up code for every error return or having explicit gotos to jump to a common error cleanup section.

However, you need to bear in mind that other people's code is not guaranteed to handle exception properly (if it's pure C, in fact it can't handle exceptions properly). Thus, you must never ever allow an exception to propagate beyond the boundaries of your code. For instance, if you throw an exception deep in the bowels of a delegate method, you must handle that exception before returning to the sender of the delegate message.

if you prefer exceptions, then you can use them. if you do, i recommend you use them sparingly because it becomes very hard to maintain (additional exit points which you'll typically have to exercise at runtime to prove the program's correctness).

there's not a specification for exception handling expectations for clients; they have to maintain their program based on the docs (tedious, error prone, probably won't be maintained until an exception is thrown).

if i were to use them, i'd use them only in very rare cases and use error codes or return nil where possible. plus, i'd use them internal to a library and not expose the clients to exceptions in the interface (or the side effects). i don't use them in objc or c++ (well, i will catch them but i won't throw them).

as you'd usually expect, you should avoid using them to control program flow (a common misuse).

it's better to use them when you have to escape a certain crash. if you're going forward and writing the code now, i recommend you just write the interface to handle the errors as part of the program flow and avoid writing exceptions where possible.

correction to original post: cocoa libs prefer to return nil, in some cases they will throw exceptions for you (to catch).