在程序中常常会遇到崩溃,我们可以通过以下两种途径来捕获崩溃信息发送到我们的服务器;这两种途径会捕获不同的崩溃信号;
途径一:
void NSSetUncaughtExceptionHandler(NSUncaughtExceptionHandler *);
Discussion
Sets the top-level error-handling function where you can perform last-minute logging before the program terminates.
途径二:
void (*signal(int, void (*)(int)))(int);
具体代码实现如下:
通过应用程序委托中调用[[AppExceptionHandler shareAppExceptionHandler] start];来开启异常捕获即可;这样,几乎所有的异常信息都能被监听并发送到自己的服务器。
#import <Foundation/Foundation.h>
@interface AppExceptionHandler : NSObject
{
@private
BOOL _dismissed;
}
+ (AppExceptionHandler*) shareAppExceptionHandler;
- (void)start;
@end
#import "AppExceptionHandler.h"
#include <execinfo.h>
#import <UIKit/UIKit.h>
static NSString * const ExceptionSourceKey = @"ExceptionSource";
static NSString * const CallStackKey = @"CallStack";
static NSString * const SignalValueKey = @"SignalValue";
static NSString * const SignalExceptionName = @"SignalExceptionName";
void InstallExceptionHandler(void);
void UncaughtExceptionHandler(NSException *exception);
void SignalExceptionHandler(int signal);
@implementation AppExceptionHandler
static AppExceptionHandler *shareAppExceptionHandler = nil;
+ (AppExceptionHandler*) shareAppExceptionHandler
{
@synchronized(self)
{
if (shareAppExceptionHandler == nil)
{
shareAppExceptionHandler = [[[self class] alloc] init];
}
}
return shareAppExceptionHandler;
}
- (id) init {
self = [super init];
if (self) {
_dismissed = NO;
}
return self;
}
- (void)start
{
// 开始程序异常监听
InstallExceptionHandler();
}
- (void)handleException:(NSException *)exception
{
// 在这里处理错误报告,例如将错误信息发送到服务器;
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"抱歉,程序出现了异常",nil)
message:NSLocalizedString(@"建议您点击退出按钮并重新打开。",nil)
delegate:self
cancelButtonTitle:NSLocalizedString(@"退出",nil)
otherButtonTitles:nil];
[alertView show];
// 等待用户响应按钮
while (!_dismissed)
{
CFRunLoopRunInMode((CFStringRef)UITrackingRunLoopMode, 0.001, false);
}
// 清理异常回调处理函数
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:SignalExceptionName])
{
kill(getpid(), [exception.userInfo[SignalValueKey] intValue]);
}
else
{
[exception raise];
}
}
+ (NSArray *)backtrace
{
void *callstack[128];
int frames = backtrace(callstack, 128);
char **strs = backtrace_symbols(callstack, frames);
NSMutableArray *traces = [NSMutableArray arrayWithCapacity:frames];
for (int i = 0; i < frames; i++)
{
[traces addObject:[NSString stringWithUTF8String:strs[i]]];
}
free(strs);
return traces;
}
#pragma mark - UIAlertViewDelegate
- (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex
{
_dismissed = YES;
}
@end
//----------------------------------
void InstallExceptionHandler(void)
{
// 捕获第一种异常,比如内存访问错误,并由UncaughtExceptionHandler回调函数处理
NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
// 捕获其他异常,比如内存过度释放之类的
signal(SIGABRT, SignalExceptionHandler);
signal(SIGILL, SignalExceptionHandler);
signal(SIGSEGV, SignalExceptionHandler);
signal(SIGFPE, SignalExceptionHandler);
signal(SIGBUS, SignalExceptionHandler);
signal(SIGPIPE, SignalExceptionHandler);
}
void UncaughtExceptionHandler(NSException *exception)
{
// 格式化程序异常时的名称,原因,调用堆栈等信息,这些信息可以返回给服务器
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[ExceptionSourceKey] = NSLocalizedString(@"UncaughtExceptionHandler",nil);
userInfo[CallStackKey] = exception.callStackSymbols;
userInfo[SignalValueKey] = [NSNumber numberWithInt:-99999];
NSException *newException = [NSException exceptionWithName:exception.name
reason:exception.reason
userInfo:userInfo];
[[AppExceptionHandler shareAppExceptionHandler] performSelectorOnMainThread:@selector(handleException:) withObject:newException waitUntilDone:YES];
}
void SignalExceptionHandler(int signal)
{
// 格式化程序异常时的名称,原因,调用堆栈等信息,这些信息可以返回给服务器
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[ExceptionSourceKey] = NSLocalizedString(@"SignalExceptionHandler",nil);
userInfo[CallStackKey] = [AppExceptionHandler backtrace];
userInfo[SignalValueKey] = [NSNumber numberWithInt:signal];
NSException *newException = [NSException exceptionWithName:SignalExceptionName
reason:[NSString stringWithFormat: NSLocalizedString(@"Signal %d was raised.",nil), signal]
userInfo:userInfo];
[[AppExceptionHandler shareAppExceptionHandler] performSelectorOnMainThread:@selector(handleException:) withObject:newException waitUntilDone:YES];
}
具体使用参考DEMO;