IOS应用崩溃复活

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 

@interface CrashHandler : NSObject
{
  BOOL ignore;
}

+ (instancetype)sharedInstance;

@end

//.m文件
#import "CrashHandler.h"
#import 
#include 
#include 

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

你可能感兴趣的