iOS 异常捕获学习

来源:互联网 发布:为知笔记vip破解版 编辑:程序博客网 时间:2024/05/16 13:39

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 {    //}@endvoid 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];}
原创粉丝点击