iOS NSException崩溃处理

it2023-03-07  93

本文主要介绍如何防止Foundation当中的常见崩溃处理

Demo地址:KJExtensionHandler
熟悉又讨厌的崩溃
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]' *** First throw call stack: ( 0 CoreFoundation 0x0000000103dca126 __exceptionPreprocess + 242 1 libobjc.A.dylib 0x0000000103c54f78 objc_exception_throw + 48 2 CoreFoundation 0x0000000103e46cdb _CFThrowFormattedException + 194 3 CoreFoundation 0x0000000103e5221e -[__NSPlaceholderDictionary initWithCapacity:].cold.1 + 0 4 CoreFoundation 0x0000000103e351f7 -[__NSPlaceholderDictionary initWithObjects:forKeys:count:] + 227 5 CoreFoundation 0x0000000103dc8da3 +[NSDictionary dictionaryWithObjects:forKeys:count:] + 49 6 KJExtensionHandler 0x00000001033b715f -[ViewController viewDidLoad] + 815 7 UIKitCore 0x000000010d7ac73b -[UIViewController _sendViewDidLoadWithAppearanceProxyObjectTaggingEnabled] + 88 8 UIKitCore 0x000000010d7b1022 -[UIViewController loadViewIfRequired] + 1084 9 UIKitCore 0x000000010d6e800e -[UINavigationController _updateScrollViewFromViewController:toViewController:] + 162 10 UIKitCore 0x000000010d6e82f8 -[UINavigationController _startTransition:fromViewController:toViewController:] + 154 11 UIKitCore 0x000000010d6e9371 -[UINavigationController _startDeferredTransitionIfNeeded:] + 851 12 UIKitCore 0x000000010d6ea6dc -[UINavigationController __viewWillLayoutSubviews] + 150 13 UIKitCore 0x000000010d6caf1e -[UILayoutContainerView layoutSubviews] + 217 14 UIKitCore 0x000000010e43d9ce -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2874 15 QuartzCore 0x0000000105546d87 -[CALayer layoutSublayers] + 258 16 QuartzCore 0x000000010554d239 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 575 17 QuartzCore 0x0000000105558f91 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 65 18 QuartzCore 0x0000000105499078 _ZN2CA7Context18commit_transactionEPNS_11TransactionEdPd + 496 19 QuartzCore 0x00000001054cfe13 _ZN2CA11Transaction6commitEv + 783 20 UIKitCore 0x000000010defe27a __34-[UIApplication _firstCommitBlock]_block_invoke_2 + 81 21 CoreFoundation 0x0000000103d385db __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 12 22 CoreFoundation 0x0000000103d379ef __CFRunLoopDoBlocks + 434 23 CoreFoundation 0x0000000103d3240c __CFRunLoopRun + 899 24 CoreFoundation 0x0000000103d31b9e CFRunLoopRunSpecific + 567 25 GraphicsServices 0x000000010c7ebdb3 GSEventRunModal + 139 26 UIKitCore 0x000000010dee0af3 -[UIApplication _run] + 912 27 UIKitCore 0x000000010dee5a04 UIApplicationMain + 101 28 KJExtensionHandler 0x00000001033ea92a main + 122 29 libdyld.dylib 0x00000001065eb415 start + 1 30 ??? 0x0000000000000001 0x0 + 1 ) libc++abi.dylib: terminating with uncaught exception of type NSException
防崩处理之后的效果
------- 😎 给我点赞 😎 ------- 编译时间:10:18:10 文件名:KJExceptionTool.m 方法名:+[KJExceptionTool kj_crashDealWithException:CrashTitle:] 行号:42 打印信息:========== crash 日志 ========== crashName: NSInvalidArgumentException crashTitle: Exception handling remove nil key-values and instance a dictionary: 字典赋值存在空 key:(null), val:123 key:key, val:(null) crashReason: *** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0] crashMessage: -[ViewController viewDidLoad]

简单介绍异常处理工具KJExceptionTool

方法一:开启全部方法交换
/// 开启全部方法交换 + (void)kj_openAllExchangeMethod{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ [NSArray kj_openExchangeMethod]; [NSMutableArray kj_openExchangeMethod]; [NSDictionary kj_openExchangeMethod]; [NSMutableDictionary kj_openExchangeMethod]; [NSMutableString kj_openExchangeMethod]; }); } /// 单个使用 [NSArray kj_openExchangeMethod]; @protocol KJExceptionProtocol <NSObject> @required /// 开启方法交换 + (void)kj_openExchangeMethod; @end
方法二:异常回调处理
static kExceptionBlock _exceptionblock = nil; + (kExceptionBlock)exceptionblock{return _exceptionblock;} + (void)setExceptionblock:(kExceptionBlock)exceptionblock{ _exceptionblock = exceptionblock; } /// 异常回调处理 + (void)kj_crashBlock:(kExceptionBlock)block{ self.exceptionblock = block; }

备注:这个只需要在最开始的地方调用一次即可

方法三:异常获取
/// 异常获取 + (void)kj_crashDealWithException:(NSException*)exception CrashTitle:(NSString*)title{ NSString *crashMessage = [self kj_analysisCallStackSymbols:[NSThread callStackSymbols]]; if (crashMessage == nil) crashMessage = @"崩溃方法定位失败,请查看函数调用栈来排查错误原因"; NSString *crashName = exception.name; NSString *crashReason = exception.reason; crashReason = [crashReason stringByReplacingOccurrencesOfString:@"avoidCrash" withString:@""]; NSLog(@"========== crash 日志 ==========\ncrashName: %@\ncrashTitle: %@\ncrashReason: %@\ncrashMessage: %@",crashName,title,crashReason,crashMessage); if (self.exceptionblock) { NSDictionary *dict = @{@"crashName":crashName, @"crashReason":crashReason, @"crashTitle":title, @"crashMessage":crashMessage, @"exception":exception, @"callStackSymbols":[NSThread callStackSymbols] }; _weakself; kGCD_main(^{weakself.exceptionblock(dict);}); } } /// 解析异常消息 + (NSString*)kj_analysisCallStackSymbols:(NSArray<NSString*>*)callStackSymbols{ __block NSString *msg = nil; NSString *pattern = @"[-\\+]\\[.+\\]";/// 匹配出来的格式为 +[类名 方法名] 或者 -[类名 方法名] NSRegularExpression *regularExp = [[NSRegularExpression alloc] initWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:nil]; for (NSInteger i = 2; i < callStackSymbols.count; i++) { NSString *matchesString = callStackSymbols[i]; [regularExp enumerateMatchesInString:matchesString options:NSMatchingReportProgress range:NSMakeRange(0, matchesString.length) usingBlock:^(NSTextCheckingResult * _Nullable result, NSMatchingFlags flags, BOOL * _Nonnull stop) { if (result) { NSString *tempMsg = [matchesString substringWithRange:result.range]; NSString *className = [tempMsg componentsSeparatedByString:@" "].firstObject; className = [className componentsSeparatedByString:@"["].lastObject; if (![className hasSuffix:@")"] && [NSBundle bundleForClass:NSClassFromString(className)] == [NSBundle mainBundle]) { msg = tempMsg; } *stop = YES; } }]; if (msg.length) break; } return msg; }

简单介绍一个分类当中的异常防崩溃处理

是否开启方法交换

+ (void)kj_openExchangeMethod{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ /// 越界崩溃方式一:[array objectAtIndex:0]; [objc_getClass("__NSArrayI") kj_swizzleMethod:@selector(objectAtIndex:) Method:@selector(kj_objectAtIndex:)]; /// 越界崩溃方式二:array[0]; [objc_getClass("__NSArrayI") kj_swizzleMethod:@selector(objectAtIndexedSubscript:) Method:@selector(kj_objectAtIndexedSubscript:)]; }); }

交换之后的处理

- (instancetype)kj_objectAtIndex:(NSUInteger)index{ NSArray *temp = nil; @try { temp = [self kj_objectAtIndex:index]; }@catch (NSException *exception) { NSString *string = @"Exception handling return nil to avoid crash: "; if (self.count == 0) { string = [string stringByAppendingString:@"数组个数为零"]; }else if (self.count <= index) { string = [string stringByAppendingString:@"数组索引越界"]; } [KJExceptionTool kj_crashDealWithException:exception CrashTitle:string]; }@finally { return temp; } } - (instancetype)kj_objectAtIndexedSubscript:(NSUInteger)index{ NSArray *temp = nil; @try { temp = [self kj_objectAtIndexedSubscript:index]; }@catch (NSException *exception) { NSString *string = @"Exception handling return nil to avoid crash: "; if (self.count == 0) { string = [string stringByAppendingString:@"数组个数为零"]; }else if (self.count <= index) { string = [string stringByAppendingString:@"数组索引越界"]; } [KJExceptionTool kj_crashDealWithException:exception CrashTitle:string]; }@finally { return temp; } }

使用介绍

[KJExceptionTool kj_openAllExchangeMethod]; [KJExceptionTool kj_crashBlock:^BOOL(NSDictionary * _Nonnull dict) { NSLog(@"回调处理:\n%@", dict[@"crashTitle"]); return YES; }]; [self.sectionTemps objectAtIndex:3]; NSMutableArray *temp = [NSMutableArray arrayWithObjects:@"1",@"2",@"3",nil]; NSString *str = nil; [temp addObject:str]; [temp setObject:@"1" atIndexedSubscript:4]; [temp insertObject:str atIndex:4]; NSDictionary *dicX = @{str:@"123", @"key":str, @"key":@"1" }; NSMutableDictionary *dict = [[NSMutableDictionary alloc]initWithObjects:@[@"1",@"1"] forKeys:@[@"2",@"2"]]; [dict setObject:str forKey:@"3"]; [dict removeObjectForKey:str];
备注:本文用到的部分函数方法和Demo,均来自三方库**KJExtensionHandler**,如有需要的朋友可自行pod 'KJExtensionHandler'引入即可

崩溃处理介绍就到此完毕,后面有相关再补充,写文章不容易,还请点个**小星星**传送门

最新回复(0)