2

So, I had an idea to catch unanticipated exceptions in main and try to cleanup and exit gracefully:

int main(int argc, char *argv[])
{
    @autoreleasepool
    {
        //return UIApplicationMain(argc, argv, nil, NSStringFromClass([GRWAppDelegate class]));
        @try
        {
            int retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([GRWAppDelegate class]));
            return retVal;
        }
        @catch (NSException *exception)
        {
            [Utilities setPreferencesDefaults];
        }
    }
}

This does catch exceptions and updates the preference defaults.

I then thought, why exit at all, just cleanup and relaunch, so I wrapped everything in a while loop:

int main(int argc, char *argv[])
{
    while (YES)
    {
        @autoreleasepool
        {
            ...
        }
    }
}

Of course I wouldn't be here if that actually worked. Problem is, once it again executes

retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([GRWAppDelegate class]));

it immediately throws a new exception:

Assertion failure in void UIApplicationInstantiateSingleton(Class)(), /SourceCache/UIKit/UIKit-2380.17/UIApplication.m:2037

NSInternalInconsistencyException
@"There can only be one UIApplication instance."

Makes sense, so is there a way I can discard the existing singleton and replace it with a new one? (although I guess it's not really a singleton if I can)

Purpose is, I don't ever want the app to crash giving a bad user experience. Even if their state isn't completely restored, I would think that would still be better than just unexpectedly exiting.

I can try to handle possible expected exceptions, but this is to try to catch things that I haven't foreseen.

This should really only catch VERY unusual circumstances, so if it can't be done it's not that big of a deal, but I was wondering how best to deal with this type of situation.

GRW
  • 605
  • 12
  • 27

2 Answers2

6

That won't work because the exception mechanism does not cleanup properly when thrown across stack frames. Since you are catching the exception in main the exception has crossed several stack frames.

Apple explicitly states that exceptions are only to be used for unrecoverable programming errors.

See SO answer by bbum: "Any exception that passes through system framework code will leave said framework in an undefined state.. Catching said exceptions and trying to recover from it will lead to memory leaks, undefined behaviour and crashing."
Also by bbum.

From Apple docs:

Important: You should reserve the use of exceptions for programming or unexpected runtime errors such as out-of-bounds collection access, attempts to mutate immutable objects, sending an invalid message, and losing the connection to the window server. You usually take care of these sorts of errors with exceptions when an application is being created rather than at runtime.

Community
  • 1
  • 1
zaph
  • 111,848
  • 21
  • 189
  • 228
  • Lots of good and interesting information, but no guidance on how to address my issue. – GRW Jan 24 '15 at 22:56
  • 2
    The "guidance" is that you can not recover from an exception in Objective-C that crosses a framework stack frame. – zaph Jan 24 '15 at 22:59
0

You can create a top level exception handler and set it as your default exception handler when you app first launches. The best place to do this would be the AppDelegate's applicationDidFinishLaunching: method.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSSetUncaughtExceptionHandler(&exceptionHandler);
    // some UI configuration here..
    return YES;
}

void exceptionHandler(NSException *exception)
{
    [Utilities setPreferencesDefaults];

    // You can also write exception message into a log file as well.
    NSLog(@"Stack trace: %@", [exception callStackReturnAddresses]); 
}
Ozgur Vatansever
  • 49,246
  • 17
  • 84
  • 119
  • Clearly the way to handle unplanned for exceptions, although the app will still exit. – GRW Jan 24 '15 at 22:55
  • 1
    @GRW If you go this way, there will be user issues because the memory will be corrupted and there is no telling how the app will work or not each time. No guarantees that `[Utilities setPreferencesDefaults];` will not corrupt the user's data. – zaph Jan 24 '15 at 23:09
  • @ozgurv One problem, I didn't downvote your answer, someone else did. – zaph Jan 27 '15 at 11:34
  • @ozgurv - Don't understand why your answer, which I have accepted, would be downvoted. It seems obvious to me that NSSetUncaughtExceptionHandler does exactly what I wanted, catch unanticipated exceptions. – GRW Jan 28 '15 at 05:19