123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- //
- // AvoidCrash.m
- // https://github.com/chenfanfang/AvoidCrash
- //
- // Created by mac on 16/9/21.
- // Copyright © 2016年 chenfanfang. All rights reserved.
- //
- #import "AvoidCrash.h"
- #define key_errorName @"errorName"
- #define key_errorReason @"errorReason"
- #define key_errorPlace @"errorPlace"
- #define key_defaultToDo @"defaultToDo"
- #define key_callStackSymbols @"callStackSymbols"
- #define key_exception @"exception"
- @implementation AvoidCrash
- + (void)becomeEffective {
- [self effectiveIfDealWithNoneSel:NO];
-
- }
- + (void)makeAllEffective {
- [self effectiveIfDealWithNoneSel:YES];
- }
- + (void)effectiveIfDealWithNoneSel:(BOOL)dealWithNoneSel {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
-
- [NSObject avoidCrashExchangeMethodIfDealWithNoneSel:dealWithNoneSel];
-
- [NSArray avoidCrashExchangeMethod];
- [NSMutableArray avoidCrashExchangeMethod];
-
- [NSDictionary avoidCrashExchangeMethod];
- [NSMutableDictionary avoidCrashExchangeMethod];
-
- [NSString avoidCrashExchangeMethod];
- [NSMutableString avoidCrashExchangeMethod];
-
- [NSAttributedString avoidCrashExchangeMethod];
- [NSMutableAttributedString avoidCrashExchangeMethod];
-
- });
- }
- + (void)setupNoneSelClassStringsArr:(NSArray<NSString *> *)classStrings {
- [NSObject setupNoneSelClassStringsArr:classStrings];
- }
- /**
- * 初始化一个需要防止”unrecognized selector sent to instance”的崩溃的类名前缀的数组
- */
- + (void)setupNoneSelClassStringPrefixsArr:(NSArray<NSString *> *)classStringPrefixs {
- [NSObject setupNoneSelClassStringPrefixsArr:classStringPrefixs];
- }
- /**
- * 类方法的交换
- *
- * @param anClass 哪个类
- * @param method1Sel 方法1
- * @param method2Sel 方法2
- */
- + (void)exchangeClassMethod:(Class)anClass method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel {
- Method method1 = class_getClassMethod(anClass, method1Sel);
- Method method2 = class_getClassMethod(anClass, method2Sel);
- method_exchangeImplementations(method1, method2);
- }
- /**
- * 对象方法的交换
- *
- * @param anClass 哪个类
- * @param method1Sel 方法1(原本的方法)
- * @param method2Sel 方法2(要替换成的方法)
- */
- + (void)exchangeInstanceMethod:(Class)anClass method1Sel:(SEL)method1Sel method2Sel:(SEL)method2Sel {
-
-
- Method originalMethod = class_getInstanceMethod(anClass, method1Sel);
- Method swizzledMethod = class_getInstanceMethod(anClass, method2Sel);
-
- BOOL didAddMethod =
- class_addMethod(anClass,
- method1Sel,
- method_getImplementation(swizzledMethod),
- method_getTypeEncoding(swizzledMethod));
-
- if (didAddMethod) {
- class_replaceMethod(anClass,
- method2Sel,
- method_getImplementation(originalMethod),
- method_getTypeEncoding(originalMethod));
- }
-
- else {
- method_exchangeImplementations(originalMethod, swizzledMethod);
- }
-
- }
- /**
- * 获取堆栈主要崩溃精简化的信息<根据正则表达式匹配出来>
- *
- * @param callStackSymbols 堆栈主要崩溃信息
- *
- * @return 堆栈主要崩溃精简化的信息
- */
- + (NSString *)getMainCallStackSymbolMessageWithCallStackSymbols:(NSArray<NSString *> *)callStackSymbols {
-
- //mainCallStackSymbolMsg的格式为 +[类名 方法名] 或者 -[类名 方法名]
- __block NSString *mainCallStackSymbolMsg = nil;
-
- //匹配出来的格式为 +[类名 方法名] 或者 -[类名 方法名]
- NSString *regularExpStr = @"[-\\+]\\[.+\\]";
-
-
- NSRegularExpression *regularExp = [[NSRegularExpression alloc] initWithPattern:regularExpStr options:NSRegularExpressionCaseInsensitive error:nil];
-
-
- for (int index = 2; index < callStackSymbols.count; index++) {
- NSString *callStackSymbol = callStackSymbols[index];
-
- [regularExp enumerateMatchesInString:callStackSymbol options:NSMatchingReportProgress range:NSMakeRange(0, callStackSymbol.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) {
- if (result) {
- NSString* tempCallStackSymbolMsg = [callStackSymbol substringWithRange:result.range];
-
- //get className
- NSString *className = [tempCallStackSymbolMsg componentsSeparatedByString:@" "].firstObject;
- className = [className componentsSeparatedByString:@"["].lastObject;
-
- NSBundle *bundle = [NSBundle bundleForClass:NSClassFromString(className)];
-
- //filter category and system class
- if (![className hasSuffix:@")"] && bundle == [NSBundle mainBundle]) {
- mainCallStackSymbolMsg = tempCallStackSymbolMsg;
-
- }
- *stop = YES;
- }
- }];
-
- if (mainCallStackSymbolMsg.length) {
- break;
- }
- }
-
- return mainCallStackSymbolMsg;
- }
- /**
- * 提示崩溃的信息(控制台输出、通知)
- *
- * @param exception 捕获到的异常
- * @param defaultToDo 这个框架里默认的做法
- */
- + (void)noteErrorWithException:(NSException *)exception defaultToDo:(NSString *)defaultToDo {
- //堆栈数据
- NSArray *callStackSymbolsArr = [NSThread callStackSymbols];
-
- //获取在哪个类的哪个方法中实例化的数组 字符串格式 -[类名 方法名] 或者 +[类名 方法名]
- NSString *mainCallStackSymbolMsg = [AvoidCrash getMainCallStackSymbolMessageWithCallStackSymbols:callStackSymbolsArr];
-
- if (mainCallStackSymbolMsg == nil) {
-
- mainCallStackSymbolMsg = @"崩溃方法定位失败,请您查看函数调用栈来排查错误原因";
-
- }
-
- NSString *errorName = exception.name;
- NSString *errorReason = exception.reason;
- //errorReason 可能为 -[__NSCFConstantString avoidCrashCharacterAtIndex:]: Range or index out of bounds
- //将avoidCrash去掉
- errorReason = [errorReason stringByReplacingOccurrencesOfString:@"avoidCrash" withString:@""];
-
- NSString *errorPlace = [NSString stringWithFormat:@"Error Place:%@",mainCallStackSymbolMsg];
-
- NSString *logErrorMessage = [NSString stringWithFormat:@"\n\n%@\n\n%@\n%@\n%@\n%@",AvoidCrashSeparatorWithFlag, errorName, errorReason, errorPlace, defaultToDo];
-
- logErrorMessage = [NSString stringWithFormat:@"%@\n\n%@\n\n",logErrorMessage,AvoidCrashSeparator];
- AvoidCrashLog(@"%@",logErrorMessage);
-
-
- //请忽略下面的赋值,目的只是为了能顺利上传到cocoapods
- logErrorMessage = logErrorMessage;
-
- NSDictionary *errorInfoDic = @{
- key_errorName : errorName,
- key_errorReason : errorReason,
- key_errorPlace : errorPlace,
- key_defaultToDo : defaultToDo,
- key_exception : exception,
- key_callStackSymbols : callStackSymbolsArr
- };
-
- //将错误信息放在字典里,用通知的形式发送出去
- dispatch_async(dispatch_get_main_queue(), ^{
- [[NSNotificationCenter defaultCenter] postNotificationName:AvoidCrashNotification object:nil userInfo:errorInfoDic];
- });
- }
- @end
|