views:

452

answers:

4

Consider the following Cocoa/Obj-C code snippets:

MyClass *obj;
@try {
 [obj doSomething];
}
@catch (NSException * e) {
 NSLog(@"Exception occurred: %@", [e description]);
}
@finally {
 [obj cleanUp];
}

and

MyClass *obj;
@try {
 [obj doSomething];
}
@catch (NSException * e) {
 NSLog(@"Exception occurred: %@", [e description]);
}
[obj cleanUp];

In what circumstances will the first snippet result in [obj cleanUp] being called, while the second won't result in [obj cleanUp] being called? In other words, in what circumstances is @finally non-redundant when using Cocoa Exception Handling?

+3  A: 

In that case, where you’re squashing the exception, none. @finally is used to clean up when you either don’t catch the exception, or rethrow it, in either case leaving final exception response to calling code. Since exceptions in Cocoa are only supposed to be used for programming errors and thus occur rarely, this is an entirely reasonably thing to do.

It’s also worth pointing out one case where you don’t need to use @finally, which is when you set up your own autorelease pool. When the “parent” autorelease pool is destroyed, any inner ones that haven’t been cleaned up yet will also be. If you do try to clean it up yourself, you need to promote the exception itself out of your autorelease pool.

Ahruman
FYI, here is Apple's doc detailing how to perform memory management with @try/@catch/@throw/@finally exception handling: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Exceptions/Tasks/HandlingExceptions.html#//apple_ref/doc/uid/20000059-SW7
Dave Gallagher
+1  A: 

When:

  • You are not catching the type of exception that occured
  • You caught the exception, but the code in the catch block also throws an exception.
eglasius
+7  A: 

In those scenarios there is no difference because the exception is swallowed. Here are two scenarios where there is a difference:

[obj cleanUp] is called:

MyClass *obj;
@try {
    [obj doSomething];
}
@catch (NSException * e) {
    @throw;      
}
@finally {
    [obj cleanUp]; // called when exception is caught
}

[obj cleanUp] is not called:

MyClass *obj;
@try {
    [obj doSomething];
}
@catch (NSException * e) {
    @throw;
}
[obj cleanUp]; // not called when exception is caught
dbarker
A: 

A lower level question, why are you doing this?

The try/catch/finally approach is widely used in Java but hardly ever used in Objective-C, and is not a preferred approach - you simply do not need it as libraries will not throw exceptions the way Java library calls would, and if you are writing your own libraries you should not expect callers will naturally think to look for exceptions to catch.

The convention most widely used and understood is that of a delegate that has an error method callback, or perhaps notifications for more general failures that you need to pass up through multiple levels of code. That approach might be more widely used in the Java world if there were a simple notification system the way Cocoa has it set up.

The delegate approach has the same documenting property as declaring an exception in Java does, they are just different approaches but it's generally better to use an approach more suited to the language at hand unless there's a very compelling reason to do otherwise.

Kendall Helmstetter Gelner
I am currently working with try/catch/finally since Apple's Scripting Bridge in OS X 10.5 regularly fails by throwing exceptions. If you use Scripting Bridge, you can't avoid exceptions.I fully agree though, for most Cocoa/Objective-C code, exceptions are not the best way of handling errors.
Nick Forge