iOS开发如何自己捕获Crash
为了在iOS中捕获和处理未捕获的Objective-C异常和系统信号引起的崩溃,可以使用`NSSetUncaughtExceptionHandler`和标准的Unix信号处理机制来实现。这能帮助你记录绝大部分的崩溃信息。以下是详细的实现步骤和代码示例:
一、系统崩溃处理
通过`NSSetUncaughtExceptionHandler`捕获未处理的Objective-C异常:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>@interface UncaughtExceptionHandler : NSObject+ (void)installUncaughtExceptionHandler:(BOOL)install showAlert:(BOOL)showAlert;@end#import "UncaughtExceptionHandler.h"
#include <libkern/OSAtomic.h>
#include <execinfo.h>NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";
NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";
NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";
volatile int32_t UncaughtExceptionCount = 0;
const int32_t UncaughtExceptionMaximum = 10;
static BOOL showAlertView = nil;void HandleException(NSException *exception);
void SignalHandler(int signal);
NSString* getAppInfo(void);@interface UncaughtExceptionHandler()
@property (assign, nonatomic) BOOL dismissed;
@end@implementation UncaughtExceptionHandler+ (void)installUncaughtExceptionHandler:(BOOL)install showAlert:(BOOL)showAlert {if (install && showAlert) {[[self alloc] alertView:showAlert];}NSSetUncaughtExceptionHandler(install ? HandleException : NULL);signal(SIGABRT, install ? SignalHandler : SIG_DFL);signal(SIGILL, install ? SignalHandler : SIG_DFL);signal(SIGSEGV, install ? SignalHandler : SIG_DFL);signal(SIGFPE, install ? SignalHandler : SIG_DFL);signal(SIGBUS, install ? SignalHandler : SIG_DFL);signal(SIGPIPE, install ? SignalHandler : SIG_DFL);
}- (void)alertView:(BOOL)show {showAlertView = show;
}+ (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)handleException:(NSException *)exception {[self validateAndSaveCriticalApplicationData:exception];if (!showAlertView) {return;}UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"出错啦"message:@"你可以尝试继续操作,但是应用可能无法正常运行."delegate:selfcancelButtonTitle:@"退出"otherButtonTitles:@"继续", nil];[alert show];CFRunLoopRef runLoop = CFRunLoopGetCurrent();CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);while (!self.dismissed) {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:UncaughtExceptionHandlerSignalExceptionName]) {kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]);} else {[exception raise];}
}- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex {if (anIndex == 0) {self.dismissed = YES;}
}- (void)validateAndSaveCriticalApplicationData:(NSException *)exception {NSString *exceptionInfo = [NSString stringWithFormat:@"\n--------Log Exception---------\nappInfo :\n%@\n\nexception name :%@\nexception reason :%@\nexception userInfo :%@\ncallStackSymbols :%@\n\n--------End Log Exception-----",getAppInfo(), exception.name, exception.reason, exception.userInfo ?: @"no user info", [exception callStackSymbols]];NSLog(@"%@", exceptionInfo);// 保存到文件等操作
}@endvoid HandleException(NSException *exception) {int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);if (exceptionCount > UncaughtExceptionMaximum) {return;}NSArray *callStack = [exception callStackSymbols];NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];[userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];[[[UncaughtExceptionHandler alloc] init]performSelectorOnMainThread:@selector(handleException:)withObject:[NSException exceptionWithName:[exception name] reason:[exception reason] userInfo:userInfo]waitUntilDone:YES];
}void SignalHandler(int signal) {int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);if (exceptionCount > UncaughtExceptionMaximum) {return;}NSString* description = nil;switch (signal) {case SIGABRT:description = @"Signal SIGABRT was raised!";break;case SIGILL:description = @"Signal SIGILL was raised!";break;case SIGSEGV:description = @"Signal SIGSEGV was raised!";break;case SIGFPE:description = @"Signal SIGFPE was raised!";break;case SIGBUS:description = @"Signal SIGBUS was raised!";break;case SIGPIPE:description = @"Signal SIGPIPE was raised!";break;default:description = [NSString stringWithFormat:@"Signal %d was raised!", signal];}NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];NSArray *callStack = [UncaughtExceptionHandler backtrace];[userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];[userInfo setObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey];[[[UncaughtExceptionHandler alloc] init]performSelectorOnMainThread:@selector(handleException:)withObject:[NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName reason:description userInfo:userInfo]waitUntilDone:YES];
}NSString* getAppInfo() {NSString *appInfo = [NSString stringWithFormat:@"App : %@ %@(%@)\nDevice : %@\nOS Version : %@ %@\n",[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"],[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],[UIDevice currentDevice].model,[UIDevice currentDevice].systemName,[UIDevice currentDevice].systemVersion];return appInfo;
}
在`didFinishLaunchingWithOptions`中调用`installUncaughtExceptionHandler`:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {[UncaughtExceptionHandler installUncaughtExceptionHandler:YES showAlert:YES];return YES;
}
二、处理Signal
在Xcode中测试Signal类型崩溃时,需在调试控制台输入以下命令,以允许Signal回调进入处理函数:
pro hand -p true -s false SIGABRT
三、测试代码
- (void)viewDidLoad {[super viewDidLoad];[self exceptionHandlerTest1];// [self exceptionHandlerTest2];// [self exceptionHandlerTest3];
}/// 异常处理测试1
-(void)exceptionHandlerTest1{NSArray *array= @[@"tom",@"xxx",@"ooo"];[array objectAtIndex:5];
}/// 异常处理测试2
-(void)exceptionHandlerTest2{[self performSelector:@selector(string) withObject:nil afterDelay:2.0];
}/// 异常处理测试3
-(void)exceptionHandlerTest3{int list[2]={1,2};int *p = list;free(p);p[1] = 5;
}