[置顶] 获取IOS应用异常崩溃日志信息


应用异常崩溃是很正常的事情,但是应用异常崩溃信息对开发者非常重要。下面就介绍如何在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

你可能感兴趣的:(NSException,NSRunLoo)