iOS 异常捕获学习

iOS 异常捕获学习

头文件

#import <UIKit/UIKit.h>

@interface CrashCatch : NSObject

+ (void)installUncaughtExceptionHandler;

+ (void)crashTest:(NSUInteger)index;

@end

实现文件

#import "CrashCatch.h"
#include <libkern/osatomic.h>
#include <execinfo.h>


NSString * const UEHSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";
NSString * const UEHSignalKey = @"UncaughtExceptionHandlerSignalKey";
NSString * const UEHAddressesKey = @"UncaughtExceptionHandlerAddressesKey";
NSString * const UEHFileKey = @"UncaughtExceptionHandlerFileKey";

volatile int32_t UncaughtExceptionCount = 0;
const int32_t UncaughtExceptionMaximum = 10;
const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4;
const NSInteger UncaughtExceptionHandlerReportAddressCount = 5;

void UncaughtExceptionHandler(NSException *exception);
void MySignalHandler(int signal);

/** The exception handler that was in place before we installed ours. */
static NSUncaughtExceptionHandler* g_previousUncaughtExceptionHandler;

@implementation CrashCatch

+ (void)installUncaughtExceptionHandler
{
    //获取其它工具注册的handler
    g_previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();

    //系统异常捕获
    NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);

    //信号量截断
    signal(SIGABRT, MySignalHandler);
    signal(SIGILL, MySignalHandler);
    signal(SIGSEGV, MySignalHandler);
    signal(SIGFPE, MySignalHandler);
    signal(SIGBUS, MySignalHandler);
    signal(SIGPIPE, MySignalHandler);
}

//异常处理方法
- (void)handleException:(NSException *)exception
{
    NSDictionary *userInfo=[exception userInfo];
    [self save:exception to:[userInfo objectForKey:UEHFileKey]];

    //注册其它工具的handler
    if (g_previousUncaughtExceptionHandler != NULL) {
        g_previousUncaughtExceptionHandler(exception);
    }

    //恢复对信号的系统默认处理
    signal(SIGABRT, SIG_DFL);
    signal(SIGILL, SIG_DFL);
    signal(SIGSEGV, SIG_DFL);
    signal(SIGFPE, SIG_DFL);
    signal(SIGBUS, SIG_DFL);
    signal(SIGPIPE, SIG_DFL);

    //
    if ([[exception name] isEqual:UEHSignalExceptionName]) {
        kill(getpid(), [[[exception userInfo] objectForKey:UEHSignalKey] intValue]);
    }
    else {
        [exception raise];
    }
}

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

    //用于获取当前线程的函数调用堆栈,返回实际获取的指针个数
    int frames = backtrace(callstack, 128);

    //从backtrace函数获取的信息转化为一个字符串数组
    char **strs = backtrace_symbols(callstack, frames);
    int i;
    NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];

    for (i = UncaughtExceptionHandlerSkipAddressCount;
         i < UncaughtExceptionHandlerSkipAddressCount+UncaughtExceptionHandlerReportAddressCount;
         i++)
    {
        [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
    }
    free(strs);

    return backtrace;
}

//获取应用信息
+ (NSString *)getAppInfo
{
    NSString *appInfo = [NSString stringWithFormat:
                         @"----------------------------------------\n"
                         @"Application  : %@ %@(%@)\n"
                         @"Device Model : %@\n"
                         @"OS Version   : %@ %@\n"
                         @"----------------------------------------\n",
                         [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"],
                         [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],
                         [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],
                         [UIDevice currentDevice].model,
                         [UIDevice currentDevice].systemName,
                         [UIDevice currentDevice].systemVersion];

    NSLog(@"AppInfo: %@", appInfo);
    return appInfo;
}

- (void)save:(NSException *)exception to:(NSString *)file
{
    // 异常堆栈信息
    NSArray *stackArray = exception.userInfo[UEHAddressesKey];

    // 异常原因
    NSString *reason = [exception reason];

    // 异常名称
    NSString *name = [exception name];

    NSString *exceptionInfo = [NSString stringWithFormat:
                               @"----------------------------------------\n"
                               @"Exception Reason   : %@\n"
                               @"Exception Name     : %@\n"
                               @"Exception Stack    : \n%@\n"
                               @"----------------------------------------\n",
                               reason, name, stackArray];

    NSLog(@"%@", exceptionInfo);


    NSLog(@">>>%@", [CrashCatch backtrace]);


    NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) [0] stringByAppendingPathComponent:file];

    if (![[NSFileManager defaultManager] fileExistsAtPath:path]){
        [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
    }

    NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970];
    NSString *savePath = [path stringByAppendingFormat:@"/error_%lld.log", (int64_t)timeInterval];

    NSError *error = nil;
    BOOL didWrite = [exceptionInfo writeToFile:savePath
                                    atomically:YES
                                      encoding:NSUTF8StringEncoding
                                         error:&error];

    if (didWrite) {
        NSLog(@"Save sucess:%@", savePath);
    }else {
        NSLog(@"Save failed:%@", error);
    }

    /**
     *  把异常崩溃信息发送至开发者邮件
     */
    NSMutableString *mailUrl = [NSMutableString string];
    [mailUrl appendString:@"mailto:example@gmail.com"];
    [mailUrl appendString:@"?subject=Report Crash"];
    [mailUrl appendFormat:@"&body=%@", exceptionInfo];

    // 打开地址
    NSString *mailPath = [mailUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailPath]];
}

+ (void)crashTest:(NSUInteger)index {
    if (index == 0) {
        //常见异常1---不存在方法引用
        [self performSelector:@selector(thisMthodDoesNotExist) withObject:nil];
    }else if (index == 1) {
        //常见异常2---键值对引用nil
        [[NSMutableDictionary dictionary] setObject:nil forKey:@"nil"];
    }else if (index == 2) {
        //常见异常3---数组越界
        [[NSArray array] objectAtIndex:1];
    }else if (index == 3) {
        //常见异常4---memory warning 级别3以上
        [[[CrashCatch alloc] init] performSelector:@selector(otherCrash) withObject:nil];
    }
}

- (void)otherCrash {
    //
}

@end

void UncaughtExceptionHandler (NSException *exception)
{
    //递增的一个全局计数器,很快很安全,防止并发数太大
    int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
    if (exceptionCount > UncaughtExceptionMaximum) { return; }

    NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
    NSArray *callStack = exception.callStackSymbols;
    [userInfo setObject:callStack forKey:UEHAddressesKey];
    [userInfo setObject:@"ObjCCrash" forKey:UEHFileKey];


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

//Signal处理方法
void MySignalHandler(int signal)
{
    //递增的一个全局计数器,很快很安全,防止并发数太大
    int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
    if (exceptionCount > UncaughtExceptionMaximum) { return; }

    NSMutableDictionary *userInfo =
    [NSMutableDictionary dictionaryWithDictionary:@{UEHSignalKey : @(signal)}];

    NSArray *callStack = [CrashCatch backtrace];
    [userInfo setObject:callStack forKey:UEHAddressesKey];
    [userInfo setObject:@"SignalCrash" forKey:UEHFileKey];

    NSString *reson = [NSString stringWithFormat:NSLocalizedString(@"Signal %d was raised.\n %@", nil),
                       signal, [CrashCatch getAppInfo]];
    NSException *exp = [NSException exceptionWithName:UEHSignalExceptionName
                                               reason:reson
                                             userInfo:userInfo];

    [[[CrashCatch alloc] init] performSelectorOnMainThread:@selector(handleException:) withObject:exp waitUntilDone:YES];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值