iOS crash捕获异常崩溃日志

在APP发布到线上后,会出现用户使用时闪退的糟糕情况。为了改进用户体验,就需要收集app崩溃的日志信息,来完善应用。
像数组越界、字典操作对象值为nil等都会发出一般异常,利用 NSSetUncaughtExceptionHandler就能捕获。但是像访问错误内存块、引用野指针等抛出的是Signal,需要做Signal处理。
下面是捕获处理这两种异常的代码:

首先,在application:(UIApplication *)application didFinishLaunchingWithOptions:方法里添加一个异常捕获的入口函数:RegisterExceptionHandler();

下面是自建CatchCrash类:

#import <Foundation/Foundation.h>

@interface CatchCrash : NSObject

//注册异常捕获
void RegisterExceptionHandler(void);

@end

。m里:

#import "CatchCrash.h"
#include <libkern/OSAtomic.h>
#include <execinfo.h>
#define CRASHREASON @"crashReason"
volatile int32_t exceptionNumber = 0;
const int32_t exceptionMaximum = 10;
@implementation CatchCrash

//获取堆栈信息
+ (NSArray *)backtrace{
    void* callstack[128];

    int frames = backtrace(callstack, 128);

    char **strs = backtrace_symbols(callstack, frames);

    NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];

    for (int i = 0; i < frames; i ++) {

        [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
    }

    free(strs);

    return backtrace;
}

#pragma mark 异常处理
- (void)handleException:(NSException *)exception{

    NSSetUncaughtExceptionHandler(NULL);
    signal(SIGABRT, SIG_DFL);
    signal(SIGILL, SIG_DFL);
    signal(SIGSEGV, SIG_DFL);
    signal(SIGFPE, SIG_DFL);
    signal(SIGBUS, SIG_DFL);
    signal(SIGPIPE, SIG_DFL);


    //捕获到异常后处理
    NSArray *callStack = [[exception userInfo]objectForKey:@"UncaughtExceptionHandlerAddressesKey"];
    NSString *reason = [exception reason];
    NSString *name = [exception name];

    //错误信息
    NSString *crashContent = [NSString stringWithFormat:@"crashName:%@ AND crashReason:%@ AND callStackSymbols:%@",name,reason,[callStack componentsJoinedByString:@"\n"]];


    /*
    //IMEI
    //手机型号
    //报错日期
    //系统版本
    */

    //有效信息自行拼接
    NSString *message = [NSString stringWithFormat:@"%@", crashContent];
    NSLog(@"*****崩溃信息log : \n%@\n",message);

    //本地存储
    NSUserDefaults *crashUser = [NSUserDefaults standardUserDefaults];
    NSString *crashPreviousContent = [crashUser objectForKey:CRASHREASON];
    if (crashPreviousContent && crashPreviousContent.length > 0) {
        [crashUser setObject:[NSString stringWithFormat:@"%@||%@", crashPreviousContent, message] forKey:CRASHREASON];
    } else {
        [crashUser setObject:message forKey:CRASHREASON];
    }
    [crashUser synchronize];

    if ([[exception name] isEqual:@"UncaughtExceptionHandlerSignalExceptionName"])
    {
        kill(getpid(), [[[exception userInfo] objectForKey:@"UncaughtExceptionHandlerSignalKey"] intValue]);
    }
    else
    {
        [exception raise];
    }
}

@end

#pragma mark 注册异常处理
void RegisterExceptionHandler(void)
{
    NSSetUncaughtExceptionHandler(&handleException);

    signal(SIGABRT, signalHandler);
    signal(SIGILL, signalHandler);
    signal(SIGSEGV, signalHandler);
    signal(SIGFPE, signalHandler);
    signal(SIGBUS, signalHandler);
    signal(SIGPIPE, signalHandler);
}

#pragma mark 普通异常处理
//处理exception报错
void handleException(NSException *exception)
{
    int32_t exceptionCount = OSAtomicIncrement32(&exceptionNumber);
    if (exceptionCount > exceptionMaximum)
    {
        return;
    }

    NSArray *callStack = [CatchCrash backtrace];
    NSMutableDictionary *userInfo =
    [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
    [userInfo setObject:callStack forKey:@"UncaughtExceptionHandlerAddressesKey"];

    [[[CatchCrash alloc] init]
     performSelectorOnMainThread:@selector(handleException:)
     withObject:[NSException exceptionWithName:[exception name]reason:[exception reason]userInfo:userInfo]waitUntilDone:YES];
}

#pragma mark 异常信号处理
//处理signal报错
void signalHandler(int signal)
{
    int32_t exceptionCount = OSAtomicIncrement32(&exceptionNumber);
    if (exceptionCount > exceptionMaximum)
    {
        return;
    }

    NSMutableDictionary *userInfo =
    [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signal]forKey:@"UncaughtExceptionHandlerSignalKey"];

    NSArray *callStack = [CatchCrash backtrace];
    [userInfo setObject:callStack forKey:@"UncaughtExceptionHandlerAddressesKey"];

    [[[CatchCrash alloc] init]
     performSelectorOnMainThread:@selector(handleException:)
     withObject:[NSException exceptionWithName:@"UncaughtExceptionHandlerSignalExceptionName"
                                        reason:[NSString stringWithFormat:@"Signal %d was raised.",
                                                signal]userInfo:userInfo]waitUntilDone:YES];
}

如果有需要,还可以在以上的代码基础上进行拓展,比如可以把存在本地的crash日志,在APP下次启动时发送给服务器,供开发者参考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值