IOS应用崩溃复活

2,383 阅读2分钟

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];

}