iOS 捕获Crash方法

作者: 紧张的牛排 | 来源:发表于2016-07-10 15:54 被阅读3858次
  • iOS开发无法避免程序Crash问题,开发过程中可以用调试技术捕获Crash进行修复,但是发布到App Store后,无法抓到这些问题,因此目前国内友盟、听云等公司抓到Crash后将Crash时的堆栈上传到他们服务器上,并可以通过dSYM文件找到Crash的方法接口。

  • 当然我们自己也可以做到,首要问题是怎么抓到这些Crash原因和堆栈列表。iOS下面最常见的就是objective-c的NSException(通过@throw抛出,比如,NSArray访问元素越界、添加空对象等等)

  • Crash堆栈


    iOS Crash堆栈

设置方法

  • 1.导入头文件
#include <signal.h>
#include <execinfo.h>
  • 2.设置Crash捕获
- (void)initHandler {
    
    struct sigaction newSignalAction;
    memset(&newSignalAction, 0,sizeof(newSignalAction));
    newSignalAction.sa_handler = &signalHandler;
    sigaction(SIGABRT, &newSignalAction, NULL);
    sigaction(SIGILL, &newSignalAction, NULL);
    sigaction(SIGSEGV, &newSignalAction, NULL);
    sigaction(SIGFPE, &newSignalAction, NULL);
    sigaction(SIGBUS, &newSignalAction, NULL);
    sigaction(SIGPIPE, &newSignalAction, NULL);
    
    //异常时调用的函数
    NSSetUncaughtExceptionHandler(&handleExceptions);
}
  • 3.异常信息
void handleExceptions(NSException *exception) {
    NSLog(@"exception = %@",exception);
    NSLog(@"callStackSymbols = %@",[exception callStackSymbols]);
}

void signalHandler(int sig) {
    //最好不要写,可能会打印太多内容
    NSLog(@"signal = %d", sig);
}
  • 4.将异常信息同步到自己服务器上
    这样就可以抓到用户使用程序时的Crash了。

实例

  • 1.在应用程序启动时初始化设置捕获,在以下方法中调用initHandler方法进行设置。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions```

- 2.写个异常程序验证,在某个控制器的```viewDidLoad```方法中写以下代码。

NSMutableArray *array = [NSMutableArray array];
[array addObject:nil];

- 3.运行代码

2016-07-10 15:44:01.148 iOSCrash[37234:6862176] exception = *** -[__NSArrayM insertObject:atIndex:]: object cannot be nil
2016-07-10 15:44:01.149 iOSCrash[37234:6862176] callStackSymbols = (
0 CoreFoundation 0x000000010ca89d85 __exceptionPreprocess + 165
1 libobjc.A.dylib 0x000000010c4fddeb objc_exception_throw + 48
2 CoreFoundation 0x000000010c94acc5 -[__NSArrayM insertObject:atIndex:] + 901
3 iOSCrash 0x000000010bffb80f -[ViewController viewDidLoad] + 111
4 UIKit 0x000000010cfda984 -[UIViewController loadViewIfRequired] + 1198
5 UIKit 0x000000010cfdacd3 -[UIViewController view] + 27
6 UIKit 0x000000010ceb0fb4 -[UIWindow addRootViewControllerViewIfPossible] + 61
7 UIKit 0x000000010ceb169d -[UIWindow _setHidden:forced:] + 282
8 UIKit 0x000000010cec3180 -[UIWindow makeKeyAndVisible] + 42
9 UIKit 0x000000010ce37ed9 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4131
10 UIKit 0x000000010ce3e568 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1769
11 UIKit 0x000000010ce3b714 -[UIApplication workspaceDidEndTransaction:] + 188
12 FrontBoardServices 0x000000010f8a88c8 FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK + 24
13 FrontBoardServices 0x000000010f8a8741 -[FBSSerialQueue _performNext] + 178
14 FrontBoardServices 0x000000010f8a8aca -[FBSSerialQueue _performNextFromRunLoopSource] + 45
15 CoreFoundation 0x000000010c9af301 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION + 17
16 CoreFoundation 0x000000010c9a522c __CFRunLoopDoSources0 + 556
17 CoreFoundation 0x000000010c9a46e3 __CFRunLoopRun + 867
18 CoreFoundation 0x000000010c9a40f8 CFRunLoopRunSpecific + 488
19 UIKit 0x000000010ce3af21 -[UIApplication _run] + 402
20 UIKit 0x000000010ce3ff09 UIApplicationMain + 171
21 iOSCrash 0x000000010bffbb6f main + 111
22 libdyld.dylib 0x000000010f26392d start + 1
)

- 4.分析Crash

>4.1 从```exception```中得知Crash原因为Array中写了一个为nil的Object。
>4.2 从```callStackSymbols```堆栈符号中找到Crash的是第2条信息,堆栈列表可以理解为第0条为正在运行的,而该列表的第22是最早运行的,因此我们可以Crash的那条往下看可以知道这条Crash发生在```[ViewController viewDidLoad]```,即```ViewController```控制器的```viewDidLoad```方法中。

- 5.注意事项

>5.1 由于```NSSetUncaughtExceptionHandler```是```set```方法,所以只能设置一次,市面上很多抓Crash的库都会设置这个方法,因此,如果你用了UMeng Crash,Bugly(腾讯云内继承Bugly)等工具的话,最好不要自己设置,会影响Crash上报。

- **如果文章对你有帮助,请点下“喜欢”吧,感谢!**

相关文章

网友评论

  • love阿木蛋花:你好! 我自己写的demo中就可以捕获crash, 在公司项目中就捕获不到. 会不会和其他的第三方有冲突导致的?
    紧张的牛排:@喝一杯咖啡 我大概知道这个监听的是系统的信号量,各种类型的信号都触发这个
    1ae3d817a065:void signalHandler(int sig) {
    //最好不要写,可能会打印太多内容
    NSLog(@"signal = %d", sig);
    } 这个方法会调用很多次 你知道为什么吗?
    紧张的牛排:@love阿木蛋花 比如bugly 会抢占handle
  • Zclee:这个crash在哪里可以看到,一定要到自己服务器?
    紧张的牛排:@Zclee自己写log文件的话,沙盒底下可以拿到,但打了release包了就不能了
  • 魔法黛:有没有demo?
    紧张的牛排:@魔法黛 就那几行代码在launch里调用就好了
  • bee_kiwi:我按照上面的例子:这个方法signalHandler 没有被调用?为什么
  • bee_kiwi:void signalHandler(int sig) {
    //最好不要写,可能会打印太多内容
    NSLog(@"signal = %d", sig);
    }
    这个方法,什么时候才会被调用?能来个例子吗
    紧张的牛排:@definemyself 这个方法我在使用中调过一次,立马打印了170M日志,具体什么情况下调用还得研究下
  • 南台:这个在真机下,拿不到堆栈信息呀,这个怎么办
    紧张的牛排:@南台 我是在iOS9上测试OK,你的是多少?然后检查下你的打印方式
    南台:@紧张的牛排 加了initHandler,到的是地址,没拿到具体的堆栈
    紧张的牛排:@简单 你的- (void)initHandler方法在哪里调用的啊?在app启动时就需要加载,AppDelegate的didFinishLaunchingWithOptions的最前面放,我在项目中使用的没有问题。请再确认下。
    注意,项目中使用不要用NSLog打印,而是将handleExceptions的NSLog方法用自己项目的写log文件的方法。
  • LinkStart:这个系统都有打印的啊,有什么额外的作用吗?
    LinkStart:@紧张的牛排 奥奥, Thanks
    紧张的牛排:@LinkStart 系统打印的你无法写到自己指定的log文件里啊。
  • 1382806c226a:请问: 最后那个分析crash是看哪里呢? 有两个iOS crash. 小弟表示不懂.
    紧张的牛排:@张先森007 上面的是我们自己打印的,下面的是调试时崩溃编辑器自动打印的
  • 萧城x: newSignalAction.sa_handler = &signalHandler; signalHandleder 哪里得到的
    紧张的牛排:@低调做事 类似handleExceptions的函数,漏掉了,待会儿补上,谢谢:smile:

本文标题:iOS 捕获Crash方法

本文链接:https://www.haomeiwen.com/subject/nqydjttx.html