应用异常崩溃是很正常的事情,但是应用异常崩溃信息对开发者非常重要。下面就介绍如何在iOS应用中捕获异常崩溃信息:

1. 程序启动中添加异常捕获监听函数,用来获取异常信息
  NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);
  官方文档介绍:Sets the top-level error-handling function where you can perform last-minute logging before the program terminates.
  UncaughtExceptionHandler是一个函数指针类型,所指向的函数需要我们实现,可以取自己想要的名字。当程序发生异常崩溃时,该函数会得到调用,这跟C,C++中的回调函数的概念是一样的。

2. 实现自己的处理函数

void UncaughtExceptionHandler(NSException *exception) {
     NSArray *arr = [exception callStackSymbols];//得到当前调用栈信息
     NSString *reason = [exception reason];//非常重要,就是崩溃的原因
     NSString *name = [exception name];//异常类型
    
     NSLog(@"exception type : %@ \n crash reason : %@ \n call stack info : %@", name, reason, arr);
 }


3. 局限
   添加异常捕获监听函数,只能监听NSException类型的异常。eg:

NSDictionary *userInfo = [[NSDictionary alloc]initWithObjectsAndKeys:@"info1", @"key1", nil];
     NSException *exception = [[NSException alloc]initWithName:@"自定义异常" reason:@"自定义异常原因" userInfo:userInfo];
     @throw exception;


    
而引起崩溃的大多数原因如:内存访问错误,重复释放等错误就无能为力了。因为这种错误它抛出的是Signal,所以必须要专门做Signal处理, 可以参考如下封装;测试时,可以调用abort()函数,模拟发送SIGABRT信号,不要联机测试,要脱机测试。

//
 //  MQLSignalHandler.h
 //  WebViewJS
 //
 //  Created by MQL on 16/4/23.
 //  Copyright © 2016年 MQL. All rights reserved.
 //

 #import <Foundation/Foundation.h>
 #include <sys/signal.h>

 @interface MQLSignalHandler : NSObject

 /**
  *  信号处理器单例获取
  *
  *  @return 信号处理器单例
  */
 + (instancetype)instance;

 /**
  *  处理异常用到的方法
  *
  *  @param exception 自己封装的异常对象
  */
 - (void)handleExceptionTranslatedFromSignal:(NSException *)exception;


 @end

 //
 //  MQLSignalHandler.m
 //  WebViewJS
 //
 //  Created by MQL on 16/4/23.
 //  Copyright © 2016年 MQL. All rights reserved.
 //

 #import "MQLSignalHandler.h"
 #import <UIKit/UIKit.h>
 #include <libkern/OSAtomic.h>
 #include <execinfo.h>

 //当前处理的异常个数
 volatile int32_t UncaughtExceptionCount = 0;
 //最大能够处理的异常个数
 volatile int32_t UncaughtExceptionMaximum = 10;

 /**
  *  捕获信号后的回调函数
  *
  *  @param signo 信号变量
  */
 void callbackHandlerOfCatchedSignal(int signo)
 {
     int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
     if (exceptionCount > UncaughtExceptionMaximum)
     {
         return;
     }
     NSMutableDictionary *userInfo =[NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signo] forKey:@"signal"];
     //创建一个OC异常对象
     NSException *ex = [NSException exceptionWithName:@"SignalExceptionName" reason:[NSString stringWithFormat:@"Signal %d was raised.\n",signo] userInfo:userInfo];
     //处理异常消息
     [[MQLSignalHandler instance] performSelectorOnMainThread:@selector(handleExceptionTranslatedFromSignal:) withObject:ex waitUntilDone:YES];
 }

 @interface MQLSignalHandler ()

 @property BOOL isDismissed;

 /**
  *  注册信号处理器
  */
 - (void)registerSignalHandler;

 @end

 @implementation MQLSignalHandler

 /**
  *  信号处理器单例获取
  *
  *  @return 信号处理器单例
  */
 + (instancetype)instance{

     static dispatch_once_t onceToken;
     static  MQLSignalHandler *s_SignalHandler =  nil;
     
     dispatch_once(&onceToken, ^{
         if (s_SignalHandler == nil) {
             s_SignalHandler  =  [[MQLSignalHandler alloc] init];
             [s_SignalHandler registerSignalHandler];
         }
     });
     return s_SignalHandler;
 }

 /**
  *  注册信号处理器
  */
 - (void)registerSignalHandler
 {
     //注册程序由于abort()函数调用发生的程序中止信号
     signal(SIGABRT, callbackHandlerOfCatchedSignal);
     //注册程序由于非法指令产生的程序中止信号
     signal(SIGILL, callbackHandlerOfCatchedSignal);
     //注册程序由于无效内存的引用导致的程序中止信号
     signal(SIGSEGV, callbackHandlerOfCatchedSignal);
     //注册程序由于浮点数异常导致的程序中止信号
     signal(SIGFPE, callbackHandlerOfCatchedSignal);
     //注册程序由于内存地址未对齐导致的程序中止信号
     signal(SIGBUS, callbackHandlerOfCatchedSignal);
     //程序通过端口发送消息失败导致的程序中止信号
     signal(SIGPIPE, callbackHandlerOfCatchedSignal);
 }

 //处理异常用到的方法
 - (void)handleExceptionTranslatedFromSignal:(NSException *)exception
 {
     CFRunLoopRef runLoop = CFRunLoopGetCurrent();
     CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
     
     UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"程序出现问题啦" message:@"崩溃信息" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:nil];
     [alertView show];
     
     //当接收到异常处理消息时,让程序开始runloop,防止程序死亡
     while (!_isDismissed) {
         for (NSString *mode in (__bridge NSArray *)allModes)
         {
             CFRunLoopRunInMode((CFStringRef)mode, 0, true);
         }
     }
     
     //当点击弹出视图的Cancel按钮哦,isDimissed = YES,上边的循环跳出
     CFRelease(allModes);
     
     //拦截处理后,执行默认处理
     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)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex
 {
     //因为这个弹出视图只有一个Cancel按钮,所以直接进行修改isDimsmissed这个变量了
     _isDismissed = YES;
 }

 @end