使用Method swizzling方法修改常用函数行为

使用Method swizzling方法修改常用函数行为

我们知道,NSArray的objectAtIndex方法若使用了一个超出范围的index值,那么将会抛出异常导致程序终止运行。

然而在发布的App中,其实NSArray的异常有些小题大做了。即使显示数据为空,也比Crash掉的用户体验要好的多啊。(例如初始化的Cell在填充数据时,objectAtIndex获取数据index越界导致异常)

因此,我就写了一个NSArray的Category:

解决问题是解决了,可是很不好看,也没有办法保证其他成员也一定使用这个方法啊。再者,这样是无法对新语法(Objective-C Literals)起作用的(这点是硬伤……)。

突然想到了之前看过的Method swizzling的方法,利用Runtime特性,可以替换现有方法的实现。在这里使用在合适不过了。(深入阅读:深入浅出Cocoa之Method Swizzling

那么试试看吧。如果你看了上面提到的那篇文章,应该发现了jrswizzle——Method swizzling方法的封装。我们就不造轮子了,用CocoaPods将它加入到我们的项目中吧。

首先创建NSArray的Category,当DEBUG模式时,index超出范围将抛出异常,而Release版本中只会返回nil:

然后在程序中调用替换方法:

悲剧,出错了:

它说没有在NSArray中找到objectAtIndex方法,Why?

这里的NSArray是一种特殊的类,英文叫做Class cluster,中文翻译过来是类簇,在设计模式中,这个叫做工厂类,它在外层提供了很多方法接口,但是这些方法的实现是由具体的内部类来实现的。当使用NSArray生成一个对象时,初始化方法会判断哪个“自己内部的类”最适合生成这个对象,然后这个“工厂”就会生成这个具体的类对象返回给你。这种又外层类提供统一抽象的接口,然后具体实现让隐藏的,具体的内部类来实现,在设计模式中称为“抽象工厂”模式。(引用自objc’s category and class cluster

也就是说,对于普通的类,我们使用上述方法是没有问题的,然而对于Class cluster这种工厂类,就需要找到它的真身才行。

通过对Class cluster的了解,我们明白了平时使用的NSArray实例其实是另外一种类型,让我们看看它们的真身:

运行查看输出,应该是如下内容:

第一行调用NSArray类方法class,当然是它自己;第二行调用[NSArray array]方法返回了一个实例,类型是__NSArrayI;第三行调用了[NSMutableArray array]方法返回了可变数组的实例,类型是__NSArrayM。

现在已经知道了待替换方法类的名称,获得它的Class就很随意了,我们有三种方法:

  • Class class = [[NSArray array] class];
  • Class class = NSClassFromString(@”__NSArrayI”);
  • Class class = objc_getClass(“__NSArrayI”);

第二种和第三种方式基本相同,都是根据类名字符串获得Class,但第三种写法编译器会报警告:Implicitly declaring library function ‘objc_getClass’ with type ‘id (const char *)’。第一种通过实体类获得Class,从安全性角度来说,我倾向于使用第一种。

最终代码如下,当然,我们还可以修改更多,这里有一份例子

这下新版本的Crash数量又降低了,想想还有点小激动呢。

扩展阅读:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值