iOS APP字典、数组异常Crash友好处理

在没有热更新的今天,相信大家都有在千辛万苦的把项目上线后,因为后台种种数据类型不符合的问题导致自己的App强行奔溃。看到这里,请对自己说,“别特么的相信后台“。一旦前端反馈出的问题,不懂技术的老板已经把锅盖扣在您亲爱的脑子上了,还怎么愉快的编写代码。不过在开发中,对Crash的情况做友好化确实是必不可少的。但是大部分常见Crash都来至字典、数组这两种数据类型,今天介绍的是几种常见的防崩手段,包括:

1.数组越界导致的崩溃
2.数组、字典有空类型时导致的崩溃
3.向Null类型取值(向一个类中调用该类不存在的方法)

其实能导致崩溃的为题还是有挺多的,一时半会想不起来。对于导致崩溃的bug,个人认为:
1.靠直觉;

2.靠对日志原因打印的分析,然后在靠直觉;

3.特么的还直觉啊?感觉断点调试啊。

  • 对于上面的问题,百度一下看起来,真的,一大堆。但是这里总结的,能够一步到位。

数组越界导致的崩溃 和 数组、字典有空类型时导致的崩溃

以下为添加类目的方式防止数组越界导致的崩溃,通过runtime捕获的异常,进入用自己写的方法替代,从而达到防止崩溃的效果。虽然比较长,但是适配了iOS9到iOS11数组越界奔溃时的名称。

+ (void)load{
    
    [super load];
    
    //  替换不可变数组中的方法
    
    Method oldObjectAtIndex = class_getInstanceMethod(objc_getClass("__NSSingleObjectArrayI"), @selector(objectAtIndex:));
    Method newObjectAtIndex = class_getInstanceMethod(objc_getClass("__NSSingleObjectArrayI"), @selector(__nickyTsui__objectAtIndex:));

    Method oldObjectAtIndex1 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
    Method newObjectAtIndex1 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(__nickyTsui2__objectAtIndex:));

    Method oldObjectAtIndex2 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndexedSubscript:));
    Method newObjectAtIndex2 = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(__nickyTsui3__objectAtIndex:));
    
    Method oldObjectAtIndex4 = class_getInstanceMethod(objc_getClass("__NSArrayM"), @selector(objectAtIndex:));
    Method newObjectAtIndex4 = class_getInstanceMethod(objc_getClass("__NSArrayM"), @selector(__nickyTsui5__objectAtIndex:));

    method_exchangeImplementations(oldObjectAtIndex, newObjectAtIndex);
    method_exchangeImplementations(oldObjectAtIndex1, newObjectAtIndex1);
    method_exchangeImplementations(oldObjectAtIndex2, newObjectAtIndex2);
    method_exchangeImplementations(oldObjectAtIndex4, newObjectAtIndex4);

    
    
    if (([UIDevice currentDevice].systemVersion.floatValue < 10.0f)){
        Method oldObjectAtIndex3 = class_getInstanceMethod(objc_getClass("__NSCFArray"), @selector(objectAtIndex:));
        Method newObjectAtIndex3 = class_getInstanceMethod(objc_getClass("__NSCFArray"), @selector(__nickyTsui4__objectAtIndex:));
        method_exchangeImplementations(oldObjectAtIndex3, newObjectAtIndex3);
}
}

- (id)__nickyTsui__objectAtIndex:(NSUInteger)index{
    if (index > self.count - 1 || !self.count){
        @try {
            return [self __nickyTsui__objectAtIndex:index];
        } @catch (NSException *exception) {
            //__throwOutException  抛出异常
//            NSLog(@"数组越界...");
            return @"";
        } @finally {
        }
    }
    else{
        return [self __nickyTsui__objectAtIndex:index];
    }
}
- (id)__nickyTsui2__objectAtIndex:(NSUInteger)index{
    if (index > self.count - 1 || !self.count){
        @try {
            return [self __nickyTsui2__objectAtIndex:index];
        } @catch (NSException *exception) {
            //__throwOutException  抛出异常
//                        NSLog(@"数组越界...");
            return @"";
        } @finally {
        }
    }
    else{
        return [self __nickyTsui2__objectAtIndex:index];
}
}
- (id)__nickyTsui3__objectAtIndex:(NSUInteger)index{
    if (index > self.count - 1 || !self.count){
        @try {
            return [self __nickyTsui3__objectAtIndex:index];
        } @catch (NSException *exception) {
            //__throwOutException  抛出异常
//                        NSLog(@"数组越界...");
            return @"";
        } @finally {
        }
    }
    else{
        return [self __nickyTsui3__objectAtIndex:index];
}
- (id)__nickyTsui4__objectAtIndex:(NSUInteger)index{
    if (index > self.count - 1 || !self.count){
        @try {
            return [self __nickyTsui4__objectAtIndex:index];
        } @catch (NSException *exception) {
            //__throwOutException  抛出异常
            //                        NSLog(@"数组越界...");
            return @"";
        } @finally {
        }
    }
    else{
        return [self __nickyTsui4__objectAtIndex:index];
    }
}
- (id)__nickyTsui5__objectAtIndex:(NSUInteger)index{
    if (index > self.count - 1 || !self.count){
        @try {
            return [self __nickyTsui5__objectAtIndex:index];
        } @catch (NSException *exception) {
            //__throwOutException  抛出异常
            //                        NSLog(@"数组越界...");
            return @"";
        } @finally {
        }
    }
    else{
        return [self __nickyTsui5__objectAtIndex:index];
    }
}

如果觉得这种方式太长还有一个是大神编写的第三方:AvoidCrash

据说有了这个工具,妈妈再也不当心App会崩溃了。简单易用,一句话开启防崩模式。
具体如下:

  • 1.最好直接pod安装或者直接拖进项目

①.pod 'AvoidCrash'
②.附上大神github
https://github.com/chenfanfang/AvoidCrash

  • 2.在AppDelegate.h 导入头文件#import "AvoidCrash.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 {
 // 1 开始监听
 [AvoidCrash becomeEffective]; 
// 2 监听通知:AvoidCrashNotification, 获取AvoidCrash捕获的崩溃日志的详细信息 
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dealwithCrashMessage:) name:AvoidCrashNotification object:nil]; return YES; 
} 

- (void)dealwithCrashMessage:(NSNotification *)note { 
//注意:所有的信息都在userInfo中
//你可以在这里收集相应的崩溃信息进行相应的处理(比如传到自己服务器) 
       NSLog(@"%@",note.userInfo);
 }

  • 3.在代码里埋bug
- (void)viewDidLoad {
    [super viewDidLoad];
   
    NSArray *testArr = @[@"1",@"2",@"3"];
    NSLog(@"_______%@_______",testArr[8]);
}
  • 4.command + R 运行,发现再也不闪退了。

不过AvoidCrash也不是万能的,当后台返回了Null类型,我们在不知该字段对应着Null的值,然后向Null类型取值或调用方法时,还是会崩溃的。

  • 这时,又一神器就要出来了。某国外大神针对Null类型的崩溃情况,当像Null调用方法时,将Null转换成了nil,nil对方法的各种调用是不会崩溃的。

附上github地址:
https://github.com/nicklockwood/NullSafe

  • 只需要把.m文件拖进项目,其他任何操作都不需要,就能生效了,喜欢吗!
  • 然后在代码里埋bug
- (void)viewDidLoad {
    [super viewDidLoad];
   
    NSDictionary *dic = @{@"123":[NSNull new]};
    
    NSLog(@"______%@______",dic[@"123"][0]);
}
  • 然后发现,从一个NSNull对象中objectAtIndex:去取值会发现不会崩溃的。

你可能感兴趣的:(iOS APP字典、数组异常Crash友好处理)