美文网首页iOS 进阶
iOS APP字典、数组异常Crash友好处理

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

作者: 小苏羿 | 来源:发表于2018-08-26 23:38 被阅读100次

    在没有热更新的今天,相信大家都有在千辛万苦的把项目上线后,因为后台种种数据类型不符合的问题导致自己的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友好处理

        本文链接:https://www.haomeiwen.com/subject/utujbftx.html