iOS NSException崩溃处理

本文主要介绍如何防止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'引入即可

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值