iOS崩溃情况
一般是由 Mach异常或 Objective-C 异常(NSException)引起的。我们可以针对这两种情况抓取对应的 Crash 事件。

Objective-C 异常
NSException异常是比较容易处理的,通过注册 NSUncaughtExceptionHandler
捕获异常信息即可
// register the uncaught exception handler
NSSetUncaughtExceptionHandler(&handler);
Mach异常
Mach是一个XNU的微内核核心,Mach异常是指最底层的内核级异常。然后Mach异常在BSD层被ux_exception
转换为相应的Unix信号,并通过threadsignal
将信号投递到出错的线程。用户可以直接通过注册异常端口来直接捕获Mach异常,当然也可以通过注册signalHandler的方式来捕获信号异常,因为操作系统会将Mach异常转换为对应的 Unix信号。
例如平常的异常EXC_BAD_ACCESS (SIGSEGV)表示的意思是:Mach层的EXC_BAD_ACCESS
异常,在BSD层被转换成SIGSEGV
信号投递到出错的线程。既然最终以信号的方式投递到出错的线程,那么就可以通过注册signalHandler来捕获信号。再如EXC_CRASH
异常,在host层会被转换成SIGABRT
信号投递出去。
在捕获Mach异常时,因为Mach异常处理会先于Unix信号处理发生,如果Mach异常的handler让程序exit了,那么Unix信号就永远不会到达这个进程了。而转换Unix信号是为了兼容更为流行的POSIX标准(SUS规范),这样就不必了解Mach内核也可以通过Unix信号的方式来兼容开发。所以对于有些Mach异常,可以直接通过 thread_set_exception_ports
注册自己的异常端口,发生异常时,首先会将异常抛给线程的异常端口
保持异常发生现场
下面代码是捕获NSException异常
// 捕获Mach异常
NSSetUncaughtExceptionHandler(&handleException);
在handleException
回调函数中,可以获取到当前的RunLoop,然后获取该RunLoop中的所有Mode,手动运行一遍。这样就保持达到拦截Crash,保证App不崩溃。
- (void)handleException:(NSException *)exception
{
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
while (YES) {//强制进入死循环
for (NSString *mode in (__bridge NSArray *)allModes) {
CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
}
}
CFRelease(allModes);
//解除监听异常
NSSetUncaughtExceptionHandler(NULL);
}
网友评论