Objective-C exceptions thrown inside methods invoked via NSInvocation are uncatchable

July 2, 2010 Adam Milligan

Whether you’re using Cedar or not, if you’ve upgraded to the iOS 4.0 SDK you may have run into some odd behavior with exception handling blocks not catching exceptions. Strangely, the problem isn’t due to the exceptions themselves (at least not in any obvious way), but with how you call functions that raise exceptions. An exception thrown from within a method you invoke directly will function as expected. However, if you invoke that same function indirectly using NSInvocation any exception thrown becomes uncatchable, crashing the current process regardless of any exception handling code.

This happens only when running against the currently available iOS 4.0 SDK. Exception handling for both direct and indirect invocations performs as expected when using the OS X 10.6 SDK and previous versions of the iPhone SDK.

You can reproduce this problem yourself easily enough. Just create any old iPhone project, and add the following code in a method you know will run (e.g. viewDidLoad on the main controller):

@try {
    NSException *exception = [NSException exceptionWithName:@"foo" reason:@"bar" userInfo:nil];
    [exception raise];
} @catch (NSException *x) {
    NSLog(@"========================> %@", x);
}

Works, right? Now try this (which should be functionally identical):

@try {
    NSException *exception = [NSException exceptionWithName:@"foo" reason:@"bar" userInfo:nil];

    SEL selector = @selector(raise);
    NSMethodSignature *signature = [exception methodSignatureForSelector:selector];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];

    [invocation setTarget:exception];
    [invocation setSelector:selector];
    [invocation invoke];
} @catch (NSException *x) {
    NSLog(@"========================> %@", x);
}

BOOM! Stack trace. Sad face.

Unfortunately, the OCHamcrest matcher library uses indirect method invocations to raise matcher failure exceptions. So, with iOS 4.0 rather than getting failure messages in your test output, you now get a stack trace and potentially a bunch of un-run tests.

Hopefully Apple will fix this soon.

About the Author

Biography

More Content by Adam Milligan
Previous
Pivotal Tracker Pro Tip: Parallel Tracks with Labels
Pivotal Tracker Pro Tip: Parallel Tracks with Labels

A frequent feature request for Pivotal Tracker is support for parallel tracks of development for multiple p...

Next
Pair Benefit #458: Choice
Pair Benefit #458: Choice

Though I am certain someone could do it, it is hard to dispute the connection between having choices and co...

Enter curious. Exit smarter.

Learn More