为了在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:self
cancelButtonTitle:@"退出"
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);
// 保存到文件等操作
}
@end
void 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;
}