iOS 异常捕获

iOS崩溃情况

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


iOS 异常捕获_第1张图片
image.png

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);
}

你可能感兴趣的:(iOS 异常捕获)