iOS - 在 main() 中捕获异常

iOS - Catching exceptions in main()

所以,我想在 maincatch 未预料到的 exceptions 并尝试清理并优雅退出:

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];
        }
    }
}

这会捕获异常并更新首选项默认值。

然后我想,为什么要退出,只是清理并重新启动,所以我将所有内容都包装在一个 while 循环中:

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

当然,如果这真的有效,我就不会在这里了。问题是,一旦它再次执行

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

它立即抛出一个新的异常:

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

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

有道理,那么有没有办法可以丢弃现有的单例并用新的单例替换它? (虽然我想如果可以的话,它不是真正的单身人士)

目的是,我不希望应用程序崩溃而给用户带来糟糕的体验。即使他们的状态没有完全恢复,我想这也比只是意外退出要好。

我可以尝试处理可能的预期异常,但这是为了尝试捕捉我没有预见到的事情。

这应该真的只捕捉非常不寻常的情况,所以如果不能完成也没什么大不了的,但我想知道如何最好地处理这种情况。

那是行不通的,因为异常机制在跨堆栈帧抛出时无法正确清理。由于您在 main 中捕获异常,异常已跨越多个堆栈帧。

Apple 明确声明异常 用于不可恢复的编程错误。

请参阅 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 来自 bbum。

来自Apple docs

重要提示:您应该将异常用于编程或意外的运行时错误,例如越界集合访问、尝试改变不可变对象、发送无效消息以及丢失与 window 服务器。您通常会在创建应用程序时而不是在运行时处理这些类型的异常错误。

您可以创建一个顶级异常处理程序,并在您的应用首次启动时将其设置为默认异常处理程序。执行此操作的最佳位置是 AppDelegate 的 applicationDidFinishLaunching: 方法。

- (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]); 
}