奔溃错误主动捕捉
- 先贴上一段源码
// LGUncaughtExceptionHandle.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface LGUncaughtExceptionHandle : NSObject
@property (nonatomic) BOOL dismissed;
+ (void)installUncaughtSignalExceptionHandler;
@end
NS_ASSUME_NONNULL_END
#import "LGUncaughtExceptionHandle.h"
#import <SCLAlertView.h>
#import <UIKit/UIKit.h>
#include <libkern/OSAtomic.h>
#include <execinfo.h>
#include <stdatomic.h>
NSString * const LGUncaughtExceptionHandlerSignalExceptionName = @"LGUncaughtExceptionHandlerSignalExceptionName";
NSString * const LGUncaughtExceptionHandlerSignalExceptionReason = @"LGUncaughtExceptionHandlerSignalExceptionReason";
NSString * const LGUncaughtExceptionHandlerSignalKey = @"LGUncaughtExceptionHandlerSignalKey";
NSString * const LGUncaughtExceptionHandlerAddressesKey = @"LGUncaughtExceptionHandlerAddressesKey";
NSString * const LGUncaughtExceptionHandlerFileKey = @"LGUncaughtExceptionHandlerFileKey";
NSString * const LGUncaughtExceptionHandlerCallStackSymbolsKey = @"LGUncaughtExceptionHandlerCallStackSymbolsKey";
atomic_int LGUncaughtExceptionCount = 0;
const int32_t LGUncaughtExceptionMaximum = 8;
const NSInteger LGUncaughtExceptionHandlerSkipAddressCount = 4;
const NSInteger LGUncaughtExceptionHandlerReportAddressCount = 5;
@implementation LGUncaughtExceptionHandle
/// Exception
void LGExceptionHandlers(NSException *exception) {
NSLog(@"%s",__func__);
int32_t exceptionCount = atomic_fetch_add_explicit(&LGUncaughtExceptionCount,1,memory_order_relaxed);
if (exceptionCount > LGUncaughtExceptionMaximum) {
return;
}
// 获取堆栈信息 - model 编程思想
NSArray *callStack = [LGUncaughtExceptionHandle lg_backtrace];
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
[userInfo setObject:exception.name forKey:LGUncaughtExceptionHandlerSignalExceptionName];
[userInfo setObject:exception.reason forKey:LGUncaughtExceptionHandlerSignalExceptionReason];
[userInfo setObject:callStack forKey:LGUncaughtExceptionHandlerAddressesKey];
[userInfo setObject:exception.callStackSymbols forKey:LGUncaughtExceptionHandlerCallStackSymbolsKey];
[userInfo setObject:@"LGException" forKey:LGUncaughtExceptionHandlerFileKey];
[[[LGUncaughtExceptionHandle alloc] init]
performSelectorOnMainThread:@selector(lg_handleException:)
withObject:
[NSException
exceptionWithName:[exception name]
reason:[exception reason]
userInfo:userInfo]
waitUntilDone:YES];
}
+ (void)installUncaughtSignalExceptionHandler{
// uncaught_handler() = fn = LGExceptionHandlers
// objc_setUncaughtExceptionHandler()
NSSetUncaughtExceptionHandler(&LGExceptionHandlers);
}
- (void)lg_handleException:(NSException *)exception{
// 保存上传服务器
NSDictionary *userinfo = [exception userInfo];
[self saveCrash:exception file:[userinfo objectForKey:LGUncaughtExceptionHandlerFileKey]];
SCLAlertView *alert = [[SCLAlertView alloc] initWithNewWindowWidth:300.f];
[alert addButton:@"奔溃" actionBlock:^{
self.dismissed = YES;
}];
[alert showSuccess:exception.name subTitle:exception.reason closeButtonTitle:nil duration:0.0f];
}
/// 保存奔溃信息或者上传
- (void)saveCrash:(NSException *)exception file:(NSString *)file{
NSArray *stackArray = [[exception userInfo] objectForKey:LGUncaughtExceptionHandlerCallStackSymbolsKey];// 异常的堆栈信息
NSString *reason = [exception reason];// 出现异常的原因
NSString *name = [exception name];// 异常名称
// 或者直接用代码,输入这个崩溃信息,以便在console中进一步分析错误原因
// NSLog(@"crash: %@", exception);
NSString * _libPath = [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:file];
if (![[NSFileManager defaultManager] fileExistsAtPath:_libPath]){
[[NSFileManager defaultManager] createDirectoryAtPath:_libPath withIntermediateDirectories:YES attributes:nil error:nil];
}
NSDate *dat = [NSDate dateWithTimeIntervalSinceNow:0];
NSTimeInterval a=[dat timeIntervalSince1970];
NSString *timeString = [NSString stringWithFormat:@"%f", a];
NSString * savePath = [_libPath stringByAppendingFormat:@"/error%@.log",timeString];
NSString *exceptionInfo = [NSString stringWithFormat:@"Exception reason:%@\nException name:%@\nException stack:%@",name, reason, stackArray];
BOOL sucess = [exceptionInfo writeToFile:savePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
NSLog(@"保存崩溃日志 sucess:%d,%@",sucess,savePath);
}
/// 获取函数堆栈信息
+ (NSArray *)lg_backtrace{
void* callstack[128];
int frames = backtrace(callstack, 128);//用于获取当前线程的函数调用堆栈,返回实际获取的指针个数
char **strs = backtrace_symbols(callstack, frames);//从backtrace函数获取的信息转化为一个字符串数组
int i;
NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
for (i = LGUncaughtExceptionHandlerSkipAddressCount;
i < LGUncaughtExceptionHandlerSkipAddressCount+LGUncaughtExceptionHandlerReportAddressCount;
i++)
{
[backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
}
free(strs);
return backtrace;
}
@end
- 使用场景:
- 当一个数组只有5个元素时,点击按钮取下标为5的元素,必然会报错且系统奔溃,因为下标最大为4.
- 所以我需要收集错误当系统不崩溃。
- (void)viewDidLoad {
[super viewDidLoad];
self.dataArray = @[@"Hank",@"CC",@"Kody",@"Cooci",@"Cat"];
}
- (IBAction)exceptionAction:(id)sender {
NSLog(@"%@",self.dataArray[5]);
}
- 使用:在app进入这个方法就调用
[LGUncaughtExceptionHandle installUncaughtSignalExceptionHandler];
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[LGUncaughtExceptionHandle installUncaughtSignalExceptionHandler];
return YES;
}
- 在这个地方打上断点后点击按钮,就可以主动捕捉到错误信息了。