程序崩溃时,在exceptionHandler中如果不做任何处理会很快退出,此时我们在这个函数里面将崩溃信息写入沙盒或上传给服务端时机是对的,但是如果我们还按照不影响主线程将耗时操作放入到子线程异步来处理的话,程序是不会等到子线程任务处理完毕再退出的,也就是说崩溃信息还没有写入沙盒或上传给服务端就退出了。
1. 截获崩溃信息(NSException)
iOS 中设置exceptionHandler,截获崩溃并显示:
// 设置全局异常处理
NSSetUncaughtExceptionHandler(&exceptionHandler);
// 全局异常处理函数
void exceptionHandler(NSException * exception) {
NSLog(@"CRASH: %@", exception);
NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
}
2. 延时推出
既然异步来不及处理,这里就使用dispatch_semaphore同步任务的方案来解决,代码如下:
// 全局异常处理函数
void exceptionHandler(NSException * exception) {
NSLog(@"CRASH: %@", exception);
NSLog(@"Stack Trace: %@", [exception callStackSymbols]);
// 使用同步处理,若异步处理需使用dispatch_semophore来阻塞当前线程
// 也可在当前 runloop 中添加任务保活线程,任务完成时退出
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[[AppDelegate new] requestTaskOrWriteToFile:^{
NSLog(@"事信号量加1");
dispatch_semaphore_signal(semaphore);
}];
//阻塞当前线程(直到 semaphore 大于 0 时退出)
dispatch_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"我可以走到这里了,之后程序就退出了");
}
//模拟进行网络请求或写入沙盒操作
- (void)requestTaskOrWriteToFile:(void(^)(void))result {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"子线程开始耗时操作");
sleep(5);
NSLog(@"子线程结束耗时操作");
result();
});
}
该思路也可以用到其他地方,如在 APP 将要 terminal 时来进行一些操作来保存用户配置到本地沙盒等。
3. 界面展示崩溃信息
在某界面显示崩溃信息时,为防止程序被强制退出,可使用以下代码,强制程序保活以致卡死:
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
while (YES) {
for (NSString *mode in (__bridge NSArray *)allModes) {
CFRunLoopRunInMode((CFStringRef)mode, 0.01, false);
}
}
一般仅限测试流程出现.....
网友评论