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:去取值会发现不会崩溃的。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,780评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,424评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,397评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,576评论 0 218
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,997评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,945评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,107评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,850评论 0 208
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,625评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,804评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,285评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,613评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,291评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,164评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,963评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,096评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,886评论 2 278

推荐阅读更多精彩内容