乐趣区

关于ios: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 <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];
}
退出移动版