3

Possible Duplicate:
does code in finally get run after a return in objective-c?

Consider this block of Objective C pseudocode:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

@try {
    throw [[NSException alloc] init];
}
@catch (NSException *e) {
    throw e;
}
@finally {
    [pool drain];
}

Will the pool be drained? Or does the throw in the @catch block render that code unreachable? I feel the pool should be drained but I can't find documentation on it one way or another.

Yes I could write some code and test it out, but at the moment that isn't feasible.

Thanks

Community
  • 1
  • 1
Madbreaks
  • 19,094
  • 7
  • 58
  • 72
  • 2
    @Perception That possible duplicate does not answer this question. That question and the docs make it clear that the finally clause is called whether there is an exception or not in the try block, but there is no mention of what happens if an exception is thrown from a catch block. It may be called, but the other question doesn't clarify this question. – rmaddy Dec 28 '12 at 23:36
  • @Madbreaks From the docs that are linked by Eugene, it looks like you should call "@throw" instead of "throw e" to rethrow the exception. The docs sort of imply (though don't clearly state) that "finally" will be called if you do this. – rmaddy Dec 28 '12 at 23:38
  • Note that you should absolutely not be catching exceptions for the purposes of recovering from them. In iOS and OS X, exceptions are used to indicate non-recoverable errors only (with a very small handful of poor odd cases). – bbum Dec 28 '12 at 23:40
  • 1
    @DanJ Since when in English do we capitalize after a colon? Please don't edit just for the sake of editing, that's not what edit is for. – Madbreaks Dec 28 '12 at 23:46
  • @bbum Where in this *pseudocode* do you perceive me attempting to recover from an exception? On the contrary, the exception is re-raised. – Madbreaks Dec 28 '12 at 23:51
  • Didn't say you were; but draining the pool is pointless. The program is dead when the exception is thrown. – bbum Dec 29 '12 at 02:33
  • +bbum I understand that. It's just arbitrary logic here, instead of `[pool drain]` it could be a `NSLog` call, or anything. The question remains (and was answered). – Madbreaks Jan 02 '13 at 18:28

2 Answers2

3

Yes (docs).

The @finally block associated with the local @catch exception handler is executed before the @throw causes the next-higher exception handler to be invoked.

See the very bottom of the linked doc page for a further note on memory management in this case. You're "OK" in your example because the exception is not itself autoreleased as part of the pool you're draining in your finally block. But you might leak that exception if no one releases it.

(But there seems to be some ambiguity around exception lifecycle in some cases, see: What is the lifecycle of an object caught in a @catch block?)

Community
  • 1
  • 1
Ben Zotto
  • 70,108
  • 23
  • 141
  • 204
  • Thanks - that's the most reassuring documentation I've seen (clearly I hadn't seen it all). I feel that docs elsewhere talk on exceptions raised in the `try` block only, which is why I had a hard time simply accepting as truth. – Madbreaks Dec 28 '12 at 23:42
  • Does the sample program that @Eugene posted not seem to contradict this? – Madbreaks Dec 28 '12 at 23:52
  • 1
    @Madbreaks No-- his example app just dies with the unhandled exception before the logger has a chance to print out to the console. If you use the same example, but invoke it from within another try/catch, you'll see the "finally" log printed out. – Ben Zotto Dec 29 '12 at 01:25
2

From https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Exceptions/Tasks/HandlingExceptions.html:

@finally — Defines a block of related code that is subsequently executed whether an exception is thrown or not.

But it says nothing about exceptions in catch blocks. It sounds logical that this exception wouldn't be cought.

I've made simple program to check that:

import <Foundation/Foundation.h>

int main(int argc, char **argv)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init] ;

    int result = 0  ;

    @try {
            @throw [NSException exceptionWithName:@"Exception" reason:@"@try" userInfo:nil];
    }
    @catch (id exception) {
            @throw [NSException exceptionWithName:@"Exception" reason:@"@catch" userInfo:nil];
    }
    @finally {
            NSLog(@"Finally");
    }

    [pool release]  ;
    return result   ;
}

Simply compile and execute that:

$ gcc -framework Foundation -fobjc-exceptions test.m
$ ./a.out 
2012-12-29 00:39:21.667 a.out[86205:707] *** Terminating app due to uncaught exception 'Exception', reason: '@catch'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff8e3050a6 __exceptionPreprocess + 198
    1   libobjc.A.dylib                     0x00007fff8e56e3f0 objc_exception_throw + 43
    2   a.out                               0x0000000107d48d47 main + 359
    3   libdyld.dylib                       0x00007fff90b4e7e1 start + 0
)
libc++abi.dylib: terminate called throwing an exception
Abort trap: 6
  • See but, I don't believe that that is what the quote is saying. I believe the quote is saying that the `finally` block will be executed whether or not a an exception is thrown in the `try` block. Accepting it as truth based on that sentence is, to me, a gray area. – Madbreaks Dec 28 '12 at 23:38
  • @BenZotto I've corrected the answer. Lost the part about catch block in original question –  Dec 28 '12 at 23:43
  • _"It sounds logical that this exception wouldn't be cought"_, right, it won't. That's not what I'm asking. I'm asking if (in your example) `"Finally"` will be logged or not. – Madbreaks Dec 28 '12 at 23:48