如何实现全局iPhone异常处理?

Coo*_*coa 59 iphone debugging cocoa-touch exception-handling objective-c

我的iPhone应用程序中有一次崩溃,它会抛出NSException.崩溃报告在错误的位置和导致错误的位置上完全不明确.有没有一种聪明的方法让我在某处设置顶级异常处理程序以查看导致它的原因?我自己无法复制问题,但我的一些beta用户当然可以.

什么是处理这种性质问题的聪明方法?

Pau*_*abe 81

看起来你在这里问两个问题:如何设置顶级异常处理程序; 以及如何处理确定根本原因的问题.

捕获异常可以通过几种不同的方式完成,但为此,最好的方法似乎是使用NSSetUncaughtExceptionHandler设置异常处理程序.

当您的应用程序引发异常时,它将由默认的异常处理程序处理.此处理程序只会在应用程序关闭之前将消息记录到控制台.您可以使用上述功能设置自己的自定义异常处理程序来覆盖它.执行此操作的最佳位置是app delegate applicationDidFinishLaunching:方法.

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
    NSSetUncaughtExceptionHandler(&myExceptionHandler);
}
Run Code Online (Sandbox Code Playgroud)

设置自定义处理程序后,您将需要扩展默认输出,以帮助您确定原因.

void myExceptionHandler(NSException *exception)
{
    NSArray *stack = [exception callStackReturnAddresses];
    NSLog(@"Stack trace: %@", stack);
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,与OSX相比,iPhone在产生良好的堆栈跟踪方面似乎非常有限.上面的代码会产生一些看似垃圾的输出; 但是,您可以通过atos工具运行此输出,并且您应该能够从中生成有用的堆栈跟踪.

另一个选择是遵循本文中的说明,将有助于自动生成一个很好的堆栈跟踪.

由于这是针对beta测试人员的,您可能需要修补一下,让它为您工作.

您说您自己无法复制问题,只能复制您的用户.在这种情况下,您可能会发现Apple的此技术说明很有用:

https://developer.apple.com/library/content/technotes/tn2151/_index.html

更新:虽然这篇文章仍然包含有用的信息,但它包含的一些链接是不可逆转的.建议使用替代帖子中的信息.

  • 当你可以做NSLog(@"%@"堆栈)时,为什么要自己迭代堆栈? (2认同)

Dur*_*n.H 12

如果您打算自己动手,可以使用其中一种方法

Approach1:

void onUncaughtException(NSException* exception)
{
//save exception details
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSSetUncaughtExceptionHandler(&onUncaughtException);
  //Add coding to find if any exception has occurred from saved details if so send it to server or ask user to comment on the issue.
//Rest of the coding
}
Run Code Online (Sandbox Code Playgroud)

Approach2:

void onUncaughtException(NSException* exception)
{

//Save exception details

}

int main(int argc, char *argv[])
{
    @autoreleasepool {

        NSSetUncaughtExceptionHandler(&onUncaughtException);

        return UIApplicationMain(argc, argv, nil, NSStringFromClass([SGGI_AppDelegate class]));
    }
}



-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 {
      //Add coding to find if any exception has occurred from saved details if so send it to server or ask user to comment on the issue.
    //Rest of the coding
 }
Run Code Online (Sandbox Code Playgroud)

Approcach3:

int main(int argc, char *argv[])
{
    @autoreleasepool {

        @try {

            return UIApplicationMain(argc, argv, nil, NSStringFromClass([SGGI_AppDelegate class]));
        }
        @catch (NSException *exception) {      
        //Save the exception
        }
        @finally {
        }

    }
}

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 {
      //Add coding to find if any exception has occurred from saved details if so send it to server or ask user to comment on the issue.
    //Rest of the coding
 }
Run Code Online (Sandbox Code Playgroud)

注意:

  • 在我的观点中,不要尝试在崩溃时将异常详细信息发送到服务器,当他再次启动应用程序时发送它.

  • 如果您打算使用NSUserDefaults保存异常详细信息,则必须在崩溃时同步它,否则它将不会持久存在.

以下代码片段完成了这项工作.

   - (void)applicationWillTerminate:(UIApplication *)application
   {
       [[NSUserDefaults standardUserDefaults]synchronize];
   }
Run Code Online (Sandbox Code Playgroud)
  • 如果你更喜欢将它保存在sqlite db上,那么它会持续存在,在崩溃时不需要调用任何东西来保存


Ken*_*ner 5

在XCode中,您应始终为其设置全局断点objc_exception_throw.然后你(通常)获得一个更有意义的堆栈跟踪,了解实际上是什么尝试抛出异常.

你仍然可以在跟踪中的任何地方获得源自计时器代码或其他地方的异常而没有你自己的代码,但是如果你看一下方法链,你通常可以弄清楚异常是什么(比如在目标中发送通知)离开了).