iOS 数组异常操作的解决办法

问题的产生

NSString *string = nil;
// 不可变数组
NSArray *array = @[string]; // 初始化中有nil对象
// 可变数组
NSMutableArray *array2 = [NSMutableArray array];
[array2 addObject:string];  // 添加nil对象
// 不可变字典
NSDictionary *dic = @{@"key":string};
// 可变字典
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setObject:string forKey:@"key"];   // 设置nil对象

上述的几个例子中,都是对数组、字典的异常操作,因为元素中都出现了nil对象,虽然我们可以在添加之前加判断去除为nil的情况,但是如果内容很多,势必会很繁琐,如果有更好的办法帮我们做完这些繁琐的事情岂不是美事?

项目中的问题

项目中可能有很多类似下面的写法

NSArray *array = @[string]; // 初始化中有nil对象
NSDictionary *dic = @{@"key":string};

有的添加了三目运算符,去掉了元素为nil的情况,但是非常的麻烦,有时候甚至会忘记,这就埋下了很多隐患。

解决方案

  • 继承

如果项目中有父类的存在,我们可以在父类中做些文章,我们可以一些新增数据操作方法,用来过滤掉一些异常操作(比如跳过nil对象部分)

  • 分类

方案一显然是不理想的,因为项目中可能存在多种父类,情况多变复杂,显然操作性太低

采用分类方式,分别新增NSArray,NSDictionary等分类文件,为其新增操作方法,在方法中过滤掉异常操作

  • 运行时

方案二较方案一有了更高的操作性,可行性,一定程度上解决了异常操作问题,但是依旧存在着不少问题,例如,
我们添加分类后,我们以后就必须使用新增的方法来操作数据,对于之前的旧代码依旧未能作出响应,假如全部替换的话,势必会产生不小的工作量,这不是我们想看到的;
另外,@[],@{}这种方式将不再可用,不,系统的部分操作方法都不可用,局限性还是很大的

那么,有没有更为优雅的方式解决上述问题呢?答案是有的,就是使用我们OC强大的运行时

基本思路:

1、使用分类
2、在 + (void)load;方法中进行方法交换
3、在自己的方法中处理掉异常

具体实现

例子1:addObject方法添加nil对象

我们先写一个异常操作

NSString *string = nil;
NSMutableArray *array = [NSMutableArray array];
[array addObject:string];

错误提示

错误提示

我们可以看到,__NSArrayM 对象调用了 -insertObject:atIndex: 产生了object cannot be nil的错误,显然易见,addObject方法最终会调用 -insertObject:atIndex: 方法,而对象不能为nil 。

接下来我们来使用运行时交换方法,处理掉这种情况

@implementation NSMutableArray (safe)
+(void)load{
    [self swizze];  
}
+(void)swizze{
    Method old = class_getInstanceMethod(NSClassFromString(@"__NSArrayM"), @selector(insertObject:atIndex:));
    Method new = class_getInstanceMethod(self, @selector(insertObject_safe:atIndex:));
    if (!old || !new) {
        return;
    }
    method_exchangeImplementations(old, new); // 交换方法
}

-(void)insertObject_safe:(id)anObject atIndex:(NSUInteger)index{
    if (index > self.count || !anObject) {
        return; // 过滤到异常部分
    }
    [self insertObject_safe:anObject atIndex:index];
}
@end

将该分类导入需要的文件中,array添加对象时就不会在出现crash问题了。

例子2:数组越界

我们使用不可变数组做例子

NSString *string = nil;
NSArray *array = @[@"0",@"1",@"2"];
NSLog(@"%@",array[5]);

报错情况

报错

对象__NSArrayI调用objectAtIndex:出现了越界。

同样的

@implementation NSArray (safe)
+(void)load{
    [self swizze];
}
+(void)swizze{
    Method old = class_getInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(objectAtIndex:));
    Method new = class_getInstanceMethod(self, @selector(objectAtIndex_safe:));
    if (!old || !new) {
        return;
    }
    method_exchangeImplementations(old, new);   // 交换方法
}
-(id)objectAtIndex_safe:(NSUInteger)index{
    if (index>=self.count) {
        return nil; // 处理异常部分
    }
    return [self objectAtIndex_safe:index];
}
@end

运行,输出

输出

我们看到,当数组越界时,仅仅是返回了 nil。

除了上述两个例子,系统中还有很多异常操作,比如数组的插入,替换,字典的setObject、字符串的操作、NSRange等等,都是待处理的部分。

总结

相比于在分类中新增方法,使用运行时捕获对应方法,会更优雅,我们不必再需要大张旗鼓的使用新方法替换旧项目中的系统方法,一劳永逸


优化部分

因为我们过滤了异常部分,无法定位错误,我们调试起来异常困难,为此,这种过滤方式最好仅仅在release模式下产生作用,而debug模式下依旧需要crash,这点可以使用宏来控制,也可以使用NSAssert断言来控制


写在最后

使用cocoaPods导入相关框架

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

推荐阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,930评论 6 13
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,131评论 30 470
  • 今天把 react-native 和 react的 版本都升级到了 各自的 最新版本,然后就遇到了 这个坑, 很多...
    蜗壳美如画阅读 952评论 2 2
  • 已经连续四天没换衣服了 因为冷或是因为懒 近期在看Quora 今天上面有人邀请我回答 中午吃的重庆小面 肉好少 只...
    徐小秋秋秋秋秋阅读 174评论 0 0
  • 文/黄小妞 01 说到梦想,我小时候的梦想是当老师,那时候觉得老师是最让人羡慕的事情,励志长大后要当一名老师,有很...
    黄小妞儿阅读 288评论 2 5