简体   繁体   中英

Objective-C try-catch - why does this compile? And why is the return different building debug vs release?

Found this interesting bit of code in an Objective-C category which is used to catch NSExceptions and pass them as NSErrors to Swift code.

What I don't understand about it is: 1) Why does it even compile? If an exception is thrown, there is never a return value. 2) Why is the return value different when compiling with debug (optimization level none) and with release (optimization level smallest/fastest)?

- (BOOL)catchException:(void(^)())tryBlock error:(__autoreleasing NSError **)error {
   @try {
       tryBlock();
       return YES;
   }
   @catch (NSException *exception) {
       NSMutableDictionary *userInfo = [exception.userInfo mutableCopy];
       if (!userInfo) userInfo = [NSMutableDictionary new];
       userInfo[NSLocalizedDescriptionKey] = exception.reason;
       *error = [[NSError alloc] initWithDomain:exception.name code:0 userInfo:userInfo];
   }
   // Note the missing return value outside the try-catch
 }

Invoking the function:

NSError *error;
BOOL result = [self catchException:^{
    @throw [NSException exceptionWithName:@"Exception" reason:@"WTF?" userInfo:nil];
} error:&error];

NSLog(@"Result: %@", result ? @"YES" : @"NO");

When compiling and running with the Debug scheme, we get:

2017-02-09 10:01:39.695 Compile Test[23129:630118] Result: NO

And when doing the same with the Release scheme:

2017-02-09 10:01:39.695 Compile Test[23129:630118] Result: YES

So in both cases there appears to be a return value, even though there is no returned value outside the try-catch block, and the return value inside the try-catch is never reached. We're all very confused over here ?!

That is either a compiler bug or the "check control flow to make sure return value is present" option is turned off (if there is one).

The differing return value is because the behavior is undefined.

Basically, whatever happens to be in the slot -- likely a register, might be on the stack, depends on the ABI of the targeted CPU -- that holds the return value at the time the function returns will be used.

Without optimization, the compiler won't reuse registers and the stack; every variable gets its own space and it is preserved to the end of the function. With optimization, the compiler will reuse memory proactively, causing the behavior change. This is also why debugging optimized code is such a pain; 'p myVariable' might print something unexpected simply because 'myVariable' has been recycled.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM