NSArray、NSMutableArray、NSDictionary、NSMutableDictionary、是我们的在iOS开发中非常常用的类。当然,在享受这些类的便利的同时,它们也给我们带来一些困扰。粗心我们可能会调用addObject:传入一个nil, 也有可能是会objectAtIndex:传入一个越界的index。尤其是在数据基本依赖于服务端返回的的情况,这种crash大幅增加。最近项目上经常出现NSDictionary的setObject:forKey:的nil object的崩溃。我们希望为这种崩溃找一个解决办法。

解决方案

函数包装

我们希望能够用一个统一的方法解决粗心的程序员可能传入的nil object。我们最先想到的想法是对这些函数进行一个包装,比如objectAtIndex,我们写一个如下的函数

- (id)safeObjectAtIndex:(NSUInteger)index {
if (index >= self.count) {
return nil;
}
return [self objectAtIndex:index];
}

以后所有调用objectAtIndex的地方统统替换为safeObjectAtIndex。 不过,这显然不是我想要的,我不希望改变现有的调用方式,大家可能是通过[]或objectAtIndex(不推荐)的方式获取数组元素,如果要做替换的话改动势必非常大,而且不便于以后的移植。我们希望代码调用objectAtIndex的时候,能够被我们先捕获到,进行处理之后再调用Cocoa的这个方法。Objective-C作为一门动态语言,有强大的动态加载的能力,提供了Method swizzling实现这样的功能。现在,黑魔法起飞。

Method swizzling

关于Method swizzling的具体实现,不打算多说,这里谈一下我遇到的问题。最初在做Method swizzling的时候,我尝试去替换NSArrat的objectAtIndex:,但我始终没有办法替换掉这个方法。后来,搞了半天才发现,我们使用的NSAarray或者NSMutableArray并不是我们所看到样子,它们是class cluster。objectAtIndex:并不是他们的方法,而是他们背后的concrete class: __NSArrayI __NSArrayM的方法。解决了这个问题,剩下的就很简单了。

使用

直接把XTSafeCollection.hXTSafeCollection.m拖入工程,NSArrayNSMutableArrayNSDictionaryNSMutableDictionary这些类的API以前是怎么调用的,还怎么写,完全不用修改。Demo里,我全部以传统的会引起crash的方式调用代码,以下是我的Demo的代码和输出

NSArray *array = @[@"a", @"b"];
NSMutableArray *mutableArray = [@[@"aa", @"bb"] mutableCopy]; // Object at index
NSLog(@"%@", array[]);
NSLog(@"%@", mutableArray[]); // add object
[mutableArray addObject:nil]; // Insert object
[mutableArray insertObject:nil atIndex:];
[mutableArray insertObject:@"cc" atIndex:]; // Replace object
[mutableArray replaceObjectAtIndex: withObject:nil];
[mutableArray replaceObjectAtIndex: withObject:@"cc"]; // Dictionary
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionary];
mutableDictionary[nil] = @"";
mutableDictionary[@""] = nil;
-- ::23.932 XTSafeCollection[:] [__NSArrayI objectAtIndex:] index {} beyond bounds [...]
-- ::23.933 XTSafeCollection[:] (null)
-- ::23.933 XTSafeCollection[:] [__NSArrayM objectAtIndex:] index {} beyond bounds [...]
-- ::23.933 XTSafeCollection[:] (null)
-- ::23.934 XTSafeCollection[:] [__NSArrayM addObject:], NIL object.
-- ::23.934 XTSafeCollection[:] [__NSArrayM insertObject:atIndex:] NIL object.
-- ::23.934 XTSafeCollection[:] [__NSArrayM insertObject:atIndex:] index {} beyond bounds [...].
-- ::23.934 XTSafeCollection[:] [__NSArrayM replaceObjectAtIndex:withObject:] NIL object.
-- ::23.934 XTSafeCollection[:] [__NSArrayM replaceObjectAtIndex:withObject:] index {} beyond bounds [...].
-- ::23.934 XTSafeCollection[:] [__NSDictionaryM setObject:forKey:] NIL key.

安装

源码: https://github.com/wuwen1030/XTSafeCollection

已知问题

替换NSMuatbelArray的objectAtIndex:引起键盘展示状态态切换后台的崩溃,抛出*** -[UIKeyboardLayoutStar release]: message sent to deallocated instance 0x7f883beac9c0 在这里找到了解决方法

iOS之利用runtime,避免可变数组和可变字典为nil或者数组越界导致的崩溃的更多相关文章

  1. iOS中利用 runtime 一键改变字体

    1.准备 我们新建一个项目名叫ChangeFont,然后我就随便找了个名叫loveway.ttf的字体库拖进去,里面的工程目录大概就是这样的 目录 现在我们就简单的直接在storyboard上拖了一个 ...

  2. 【iOS】利用Runtime特性做监控

    最近在看Object-C运行时特性,其中有一个特别好用的特性叫 Method Swizzling ,可以动态交换函数地址,在应用程序加载的时候,通过运行时特性互换两个函数的地址,不改变原有代码而改变原 ...

  3. iOS开发,利用文件流,算大文件的MD5值(程序不会导致内存崩溃)

    CFStringRef FileMD5HashCreateWithPath(CFStringRef filePath, size_t chunkSizeForReadingData) { // Dec ...

  4. iOS利用Runtime自定义控制器POP手势动画

    前言 苹果在iOS 7以后给导航控制器增加了一个Pop的手势,只要手指在屏幕边缘滑动,当前的控制器的视图就会跟随你的手指移动,当用户松手后,系统会判断手指拖动出来的大小来决定是否要执行控制器的Pop操 ...

  5. ios开发runtime学习五:KVC以及KVO,利用runtime实现字典转模型

    一:KVC和KVO的学习 #import "StatusItem.h" /* 1:总结:KVC赋值:1:setValuesForKeysWithDictionary实现原理:遍历字 ...

  6. iOS - 利用runtime加深对基础知识的理解

    利用runtime加深对基础知识的理解 如果对runtime需要学习,可以看这篇,以下仅作为学习笔记,相互交流. runtime的头文件: #import <objc/runtime.h> ...

  7. iOS -Swift 3.0 -Array(数组与可变数组相关属性及用法)

    // // ViewController.swift // Swift-Array // // Created by luorende on 16/9/12. // Copyright © 2016年 ...

  8. ios 利用runtime任性跳转

    在开发项目中,会有这样变态的需求: 推送:根据服务端推送过来的数据规则,跳转到对应的控制器 feeds列表:不同类似的cell,可能跳转不同的控制器(嘘!产品经理是这样要求:我也不确定会跳转哪个界面哦 ...

  9. iOS运行时Runtime浅析

    运行时是iOS中一个很重要的概念,iOS运行过程中都会被转化为runtime的C代码执行.例如[target doSomething];会被转化成objc)msgSend(target,@select ...

随机推荐

  1. Luogu1261: 服务器储存信息问题

    题面 传送门 Sol 我们可以考虑每种\(rank\)的点\(u\)会被哪些点\(v\)感兴趣 如果\(dis[u][v]<\)所有满足\(rank\)大于\(rank[u]\)的点到\(v\) ...

  2. CentOS/ubuntu/Solaris软件包安装

    一.CentOS/Red Hat yum = Yellow dog Updater, Modified     (1)yum配置文件      (在CentOS下,默认安装yum,无须配置即可使用) ...

  3. C# 修改GroupBox的边框颜色和字体颜色

    改变GroupBox边框和的颜色 private void groupBox_BasicInformation_Paint(object sender, PaintEventArgs e) { e.G ...

  4. ToolBar 简单使用

    ToolBar 简单使用 ToolBar 是在 android 5.0之后推出的一款用来替代 ActionBar 的 View.ActionBar 是Activity的一部分,不能用在其他视图层次上( ...

  5. Quick Easy FTP Server FTP工具文件传输使用

    1 工具配置 2 linux 下ftp命令上传和下载文件到FTP_DIR目录 1 ftp 172.16.18.292 输入用户名3 输入密码 4 ls或dir 查看目录及文件  lcd进入本地目录  ...

  6. 【NLP_Stanford课堂】文本分类1

    文本分类实例:分辨垃圾邮件.文章作者识别.作者性别识别.电影评论情感识别(积极或消极).文章主题识别及任何可分类的任务. 一.文本分类问题定义: 输入: 一个文本d 一个固定的类别集合C={c1,c2 ...

  7. .Net中初探Redis

    一.简介 Redis是著名的NOSQL数据库,本质就是存储键值对结构的数据,为存储键值对数据做了优化,在大型网站中应用很多.Redis提供了数据的自动过期处理,因此适合存储临时数据. 和Redis类似 ...

  8. Anaconda套件,精簡版miniconda

    雖然Anaconda會預先安裝豐富的套件模組,尤其是在數據科學領域方面,有非常豐富的寶藏, 大多範例或教學或許為了節省後續的麻煩,不解釋為什麼,直接就安裝Anaconda 就對了: 但是大部份的模組套 ...

  9. 设计模式:备忘录(Memento)模式

    设计模式:备忘录(Memento)模式 一.前言   备忘录模式用于保存和恢复对象的状态,相信大家看过我前面的拙作就会想到原型模式也能保存一个对象在某一个时刻的状态,那么两者有何不同的呢?原型模式保存 ...

  10. 新款 2018款macbook Pro 装双系统教程

    首个阅读量将破万的文章,感谢支持.防止无良爬虫,开头附上原文链接:http://www.cnblogs.com/xueyudlut/p/7498115.html ------------------- ...