【问题标题】:How do you implement global iPhone Exception Handling?如何实现全局 iPhone 异常处理?
【发布时间】:2010-11-19 22:07:44
【问题描述】:

我的 iPhone 应用程序发生了一次崩溃,它确实引发了 NSException。崩溃报告在错误在哪里以及究竟是什么导致错误方面完全模棱两可。我是否有一种聪明的方法可以在某处设置顶级异常处理程序以查看导致它的原因?我自己无法复制这个问题,但我的一些测试版用户肯定可以。

处理此类问题的明智方法是什么?

【问题讨论】:

    标签: iphone objective-c cocoa-touch debugging exception-handling


    【解决方案1】:

    您似乎在这里问了两个问题:如何设置顶级异常处理程序;以及如何处理确定根本原因的问题。

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

    当您的应用程序中引发异常时,它由默认异常处理程序处理。这个处理程序只是在应用程序关闭之前将消息记录到控制台。您可以通过使用上述函数设置您自己的自定义异常处理程序来覆盖它。执行此操作的最佳位置是在应用程序委托 applicationDidFinishLaunching: 方法中。

    - (void)applicationDidFinishLaunching:(UIApplication *)application
    {
        NSSetUncaughtExceptionHandler(&myExceptionHandler);
    }
    

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

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

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

    另一种选择是遵循this article 上的说明,这将有助于自动生成良好的堆栈跟踪。

    由于这是面向 Beta 版测试人员的,您可能需要修补一下才能让它为您工作。

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

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

    更新:虽然这篇文章仍然包含有用的信息,但其中包含的一些链接已不可逆转地失效。建议使用this替代帖子中的信息。

    【讨论】:

    • Google Toolbox for Mac 的 GTMStackTrace 有时会提供很大帮助 :)
    • 当你可以做 NSLog(@"%@" stack) 时,为什么要自己遍历堆栈?
    • 基于缺乏反馈,我认为这仍然是一个问题。我添加了对 Apple 技术说明的参考,可能会有所帮助。
    • myExceptionHandler 必须运行多少个周期?我正在尝试显示 UIAlertView 但应用程序在显示之前完全死机。你能强制 myExceptionHandler 占用更多周期吗?
    • 不应该是一个函数,即 void myExceptionHandler (NSException *exception)
    【解决方案2】:

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

    方法1:

    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
    }
    

    方法二:

    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
     }
    

    方法3:

    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
     }
    

    注意:

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

    • 如果您要使用 NSUserDefaults 来保存异常详细信息,那么您必须在崩溃时同步它,否则它不会持续存在。

    下面的代码 sn-p 完成这项工作。

       - (void)applicationWillTerminate:(UIApplication *)application
       {
           [[NSUserDefaults standardUserDefaults]synchronize];
       }
    
    • 如果您更愿意将其保存在 sqlite db 上,那么它会自行持久化,无需在崩溃时调用任何东西来持久化

    【讨论】:

    • 这对任何人都有效吗? Cuz 对我不起作用 :( 我尝试在 NSUserDefaults onUncaughtException 方法中保存 nsarray obj / callStackSymbols。在接下来的 didFinishLaunchingWithOptions 中,我无法获取用于保存在 NSUserDefaults 中的密钥的任何对象。
    • @Abhijit - 保存后同步 ([[NSUserDefaults standardUserDefaults]synchronize])
    • :D 虽然我忘了提到这一点,但我当然记得同步 Userdefaults
    • 正是我需要看到的。太好了,杜莱。
    • 重新运行应用后,NSUserDefault 中的数据返回 nil...我不知道为什么
    【解决方案3】:

    在 XCode 中,您应该始终为 objc_exception_throw 设置全局断点。然后,您(通常)会获得更有意义的堆栈跟踪,以了解实际尝试抛出异常的原因。

    您仍然可以在跟踪中的任何位置没有您自己的代码的情况下获取源自计时器代码或其他地方的异常,但是如果您查看方法链,您通常可以大致了解异常的含义(例如发送通知目标消失的地方)。

    【讨论】:

    • 这仅在使用 Xcode 运行应用程序时有用吗?不在生产应用程序上(例如,将异常写入日志文件)。
    • 是的,仅在 XCode 运行应用程序时有用。
    【解决方案4】:

    跟踪崩溃报告的另一个选项是 Plausible CrashReporter,它是一种开源代码,可自动从现场向您发送崩溃报告。

    还有 CrashReporterDemo,这是另一个开源选项,它结合了 Plausible CrashReporter 和一些服务器代码,可以更好地跟踪崩溃报告。

    最后,还有另一个答案中建议的 MacDevCrashReporter,该服务似乎与 iOSExceptional.com 有相似之处。我不知道他们的服务条款是什么,因为我还没有注册测试版。在深入了解之前绝对值得检查。

    【讨论】:

      【解决方案5】:

      【讨论】:

      • KP 很不错,但提到如何实现它更有帮助。
      • 链接已损坏
      【解决方案6】:

      查看Crittercism。它超出了您在此处所要求的范围,因为它可以让您为使用您的应用的所有用户获取此信息,因此您应该能够看到自己的崩溃。

      您还可以为您的特定构建上传 DYSM,它会自动在他们的网站上为您表示崩溃。这应该为您提供最清晰的堆栈跟踪,而无需连接到调试器。

      您可能还想确保您已设置为在 Objetive-C 异常中中断。在 Xcode 4 中,在断点选项卡中,您可以添加在 C++ 和 Obj-C 异常上都中断的断点异常。如果不启用此选项,大多数抛出异常的堆栈跟踪都将毫无帮助。

      祝你好运!

      【讨论】:

        猜你喜欢
        • 2020-12-21
        • 2016-11-03
        • 2021-08-19
        • 1970-01-01
        • 2015-12-21
        • 2011-09-29
        • 2011-06-17
        • 1970-01-01
        相关资源
        最近更新 更多