我的iPhone应用程序中有一次崩溃,它会抛出NSException。崩溃报告在错误的位置和导致错误的位置上完全不明确。有没有一种聪明的方法让我在某处设置顶级异常处理程序以查看导致它的原因?我不能自己复制这个问题,但我的一些beta用户当然可以。
处理这种问题的聪明方法是什么?
答案 0 :(得分:80)
您似乎在这里提出两个问题:如何设置顶级异常处理程序;以及如何处理确定根本原因的问题。
捕获异常可以通过几种不同的方式完成,但为此,最好的方法似乎是使用NSSetUncaughtExceptionHandler设置异常处理程序。
当您的应用程序引发异常时,它由默认的异常处理程序处理。此处理程序只会在应用程序关闭之前将消息记录到控制台。您可以使用上述功能设置自己的自定义异常处理程序来覆盖它。执行此操作的最佳位置是app delegate applicationDidFinishLaunching:method。
- (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替代帖子中的信息。
答案 1 :(得分:12)
如果您打算自己动手,可以使用其中一种方法
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
}
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来保存异常详细信息,那么您必须在崩溃时同步它,否则它将不会持续存在。
以下代码段完成了这项工作。
- (void)applicationWillTerminate:(UIApplication *)application
{
[[NSUserDefaults standardUserDefaults]synchronize];
}
答案 2 :(得分:5)
在XCode中,您应始终为objc_exception_throw
设置全局断点。然后你(通常)得到一个更有意义的堆栈跟踪,看看实际上是什么尝试抛出异常。
你仍然可以在跟踪中的任何地方获得源自计时器代码或其他地方的异常而没有你自己的代码,但是如果你查看方法链,你通常可以弄清楚异常是什么(比如发送通知)目标消失的地方)。
答案 3 :(得分:2)
答案 4 :(得分:2)
跟踪崩溃报告的另一个选项是 Plausible CrashReporter ,这是一个开源代码,可以自动向您发送崩溃报告。
还有 CrashReporterDemo ,这是另一个开源选项,它是Plausible CrashReporter和一些服务器代码的组合,可以更好地跟踪崩溃报告。
最后,还有 MacDevCrashReporter ,这项服务似乎与iOSExceptional.com有相似之处,在另一个答案中提出。我不知道他们的服务条款是什么,因为我没有注册测试版。在深入了解之前绝对值得检查。
答案 5 :(得分:2)
结帐Crittercism。它超出了您的要求,它可以让您使用您的应用程序为所有用户获取此信息,因此您应该能够看到自己的崩溃。
您还可以为您的特定版本上传DYSM,它会自动在您的网站上为您表示崩溃。这应该为您提供最清晰的堆栈跟踪,而无需连接到调试器。
您可能还想确保设置为中断Objetive-C异常。在Xcode 4中,在breakpoints选项卡中,您可以添加一个断点异常,该异常断开C ++和Obj-C异常。如果没有这个,抛出异常的大多数堆栈跟踪都是非常无益的。
祝你好运!