IOS应用崩溃一直是一个很苦恼的问题,而崩溃的同时又未获取bug原因,更令人苦恼。
好在苹果自带的crash检测api,以及runloop可以让应用复活一次,不过第二次依旧会崩溃,但是还是一个很实用的小技巧。
以下是IOS崩溃复活的类和使用方法
#import "CrashHandler.h" //Appdelagte中注册crash记录上报 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. [CrashHandler sharedInstance]; return YES; } //.h文件 #import <Foundation/Foundation.h> @interface CrashHandler : NSObject { BOOL ignore; } + (instancetype)sharedInstance; @end //.m文件 #import "CrashHandler.h" #import <UIKit/UIKit.h> #include <libkern/OSAtomic.h> #include <execinfo.h> NSString * const kSignalExceptionName = @"kSignalExceptionName"; NSString * const kSignalKey = @"kSignalKey"; NSString * const kCaughtExceptionStackInfoKey = @"kCaughtExceptionStackInfoKey"; void HandleException(NSException *exception); void SignalHandler(int signal); @implementation CrashHandler static CrashHandler *instance = nil; + (instancetype)sharedInstance { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[[self class] alloc] init]; }); return instance; } + (instancetype)allocWithZone:(struct _NSZone *)zone { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [super allocWithZone:zone]; }); return instance; } - (instancetype)init { self = [super init]; if (self) { [self setCatchExceptionHandler]; } return self; } - (void)setCatchExceptionHandler { // 1.捕获一些异常导致的崩溃 NSSetUncaughtExceptionHandler(&HandleException); // 2.捕获非异常情况,通过signal传递出来的崩溃 signal(SIGABRT, SignalHandler); signal(SIGILL, SignalHandler); signal(SIGSEGV, SignalHandler); signal(SIGFPE, SignalHandler); signal(SIGBUS, SignalHandler); signal(SIGPIPE, SignalHandler); } + (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; } - (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex { if (anIndex == 0) { ignore = YES; } else if (anIndex == 1) { NSLog(@"起死回生"); } } - (void)handleException:(NSException *)exception { NSString *message = [NSString stringWithFormat:@"崩溃原因如下:\n%@\n%@", [exception reason], [[exception userInfo] objectForKey:kCaughtExceptionStackInfoKey]]; NSLog(@"%@",message); UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"程序崩溃了" message:@"如果你能让程序起死回生,那你的决定是?" delegate:self cancelButtonTitle:@"崩就蹦吧" otherButtonTitles:@"起死回生", nil]; [alert show]; CFRunLoopRef runLoop = CFRunLoopGetCurrent(); CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop); while (!ignore) { for (NSString *mode in (__bridge NSArray *)allModes) { CFRunLoopRunInMode((CFStringRef)mode, 0.001, false); } } CFRelease(allModes); 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); if ([[exception name] isEqual:kSignalExceptionName]) { kill(getpid(), [[[exception userInfo] objectForKey:kSignalKey] intValue]); } else { [exception raise]; } } @end void HandleException(NSException *exception) { // 获取异常的堆栈信息 NSArray *callStack = [exception callStackSymbols]; NSMutableDictionary *userInfo = [NSMutableDictionary dictionary]; [userInfo setObject:callStack forKey:kCaughtExceptionStackInfoKey]; CrashHandler *crashObject = [CrashHandler sharedInstance]; NSException *customException = [NSException exceptionWithName:[exception name] reason:[exception reason] userInfo:userInfo]; [crashObject performSelectorOnMainThread:@selector(handleException:) withObject:customException waitUntilDone:YES]; } void SignalHandler(int signal) { // 这种情况的崩溃信息,就另某他法来捕获吧 NSArray *callStack = [CrashHandler backtrace]; NSLog(@"信号捕获崩溃,堆栈信息:%@",callStack); CrashHandler *crashObject = [CrashHandler sharedInstance]; NSException *customException = [NSException exceptionWithName:kSignalExceptionName reason:[NSString stringWithFormat:NSLocalizedString(@"Signal %d was raised.", nil),signal] userInfo:@{kSignalKey:[NSNumber numberWithInt:signal]}]; [crashObject performSelectorOnMainThread:@selector(handleException:) withObject:customException waitUntilDone:YES]; }