iOS应用崩溃,常见的崩溃信息有 EXC_BAD_ACCESS
、 SIGABRT XXXXXXX
,而这里分为两种情况,一种是未被捕获的异常,我们只需要添加一个回调函数,并在应用启动时调用一个 API即可; 另
一种是直接发送的 SIGABRT XXXXXXX
,这里我们也需要监听各种信号,然后添加回调函数。
针对情况一,其实我们都见过。 我们在收集App崩溃信息时,需要添加一个函数 NSSetUncaughtExceptionHandler(&HandleException)
,参数 是一个回调函数,在回调函数里获取到异常的
原因,当前的堆栈信息等保存到 dump文件,然后供下次打开App时上传到服务器。
其实,我们在HandleException回调函数中,可以获取到当前的RunLoop,然后获取该RunLoop中的所有Mode,手动运行一遍。
针对情况二,首先针对多种要捕获的信号,设置好回调函数,然后也是在回调函数中获取RunLoop,然后拿到所有的Mode,手动运行一遍。
代码实现
第一步,我创建了一个处理类 ZSExceptionHandler ,并添加一个单例方法。
+ (instancetype)shareInstance { static ZSExceptionHandler *handler = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ handler = [[self alloc] init]; }); return handler; }
第二步,在单例中对象实例化时,添加 异常捕获 和 signal 处理的 回调函数。
- (void)startCatchExceptionHandler { // 1.捕获一些异常导致的崩溃 NSSetUncaughtExceptionHandler(&HandleException); // 2.捕获非异常情况,通过signal传递出来的崩溃 signal(SIGABRT, SignalHandler); signal(SIGILL, SignalHandler); signal(SIGSEGV, SignalHandler); signal(SIGFPE, SignalHandler); signal(SIGBUS, SignalHandler); signal(SIGPIPE, SignalHandler); }
第三步,分别实现 异常捕获的回调 和 signal 的回调。里面可以都封装成异常模型 NSException
void HandleException(NSException *exception) { ZSExceptionHandler *obj = ZSExceptionHandler.shareInstance; [obj handleException]; } void SignalHandler(int signal) { ZSExceptionHandler *obj = ZSExceptionHandler.shareInstance; [obj handleException]; }
第四步,在崩溃前上传数据或者操作UI
-(void) handleException { //如果有操作UI的地方 需要放到主线程 @synchronized (self) { UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"程序崩溃了" message:@"如果你能让程序起死回生,那你的决定是?" preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction *sureAction = [UIAlertAction actionWithTitle:@"崩就蹦吧" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { }]; UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"起死回生" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { }]; [alertController addAction:sureAction]; [alertController addAction:cancelAction]; [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil]; } //每种mode需要延迟3s去上传数据到后台 BOOL need = YES; CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop); if (need) { for (NSString *mode in (__bridge NSArray *)allModes) { CFRunLoopRunInMode((CFStringRef)mode, 3, false); } } CFRelease(allModes); NSSetUncaughtExceptionHandler(NULL); signal(SIGABRT, SIG_DFL); signal(SIGILL, SIG_DFL); signal(SIGSEGV, SIG_DFL); signal(SIGFPE, SIG_DFL); signal(SIGBUS, SIG_DFL); signal(SIGPIPE, SIG_DFL); }
第五步,写一段会导致崩溃的代码
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { NSArray *array =[NSArray array]; NSLog(@"%@",[array objectAtIndex:1]); }
第六步,App启动时,开启崩溃捕获
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. [ZSExceptionHandler.shareInstance startCatchExceptionHandler]; return YES; }