【IOS】崩溃复活

本文探讨了iOS应用在崩溃时如何利用Apple的crash检测API和runloop进行临时复活的技术,虽然只能复活一次,但该技巧对于捕获崩溃信息和提供用户体验有一定帮助。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值