Handling NSInvalidArgumentException “Pushing the same view controller instance more than once is not supported” in an iOS application
When debugging my iOS app, I discovered that the following apparently innocent code sometimes caused the app to crash due to an NSInvalidArgumentException, “Pushing the same view controller instance more than once is not supported”:
[devNavController pushViewController:myVC animated:YES];
The crash happens when there are too many opened applications and as the error message suggests, it happens because the user clicks on the same button again causing the code to push the same view controller twice into the navigation controller, which is prohibited in iOS.
My first attempt is to handle the crash with try/catch:
@try { [devNavController pushViewController:myVC animated:YES]; } @catch (NSException * e) { NSLog(@"Exception: %@", e); }
Although you may say that try/catch should never be used in Objective C as all possible error conditions should have been checked prior to executing codes that might generate exceptions, this is not possible in many cases. After all, how can a programmer predict all possible error scenarios that might happen? And if programmers are indeed not supposed to use try/catch in Objective-C, why does the language still have such a construct? In my case, I have successfully used try/catch many times in Objective-C in non-critical background codes where all that is needed is to hide the error from the users. For example, in one of my applications, an exception in a background task that updates the contact autocomplete database (as retrieved from the phone’s address book) can probably be ignored as the same task can be executed again without any major impact on application functionality since the user can still live without autocomplete for a while and simply type the number manually.
I proceeded to run the modified code with try/catch, and guess what, the exception still crashed the app, as if the try/catch construct was never added. The only thing that worked in the end is to check if the same view controller is already to the navigation stack, and do not push it again if this is the case:
if(![self.navigationController.topViewController isKindOfClass:[MyViewController class]]) { [devNavController pushViewController:myVC animated:YES]; }
But why didn’t try/catch work in the first place? Perhaps the above NSInvalidArgumentException behaves likes java.lang.Error in the Java language. That is, it can only be handled by catching whatever is the equivalent on java.lang.Error in Objective-C, and not by catching NSException like what I tried in the above code. Or perhaps this is simply an iOS bug. Whatever the reason, this is just another quirk in the Objective-C language that developers simply have to live with.