iOS系统的一些方法的实现效果不尽人意,例如数组越界苹果就会直接crash,不会像字典一样置空,程序就会闪退,用户体验会很不好。有些时候数组越界了,但我们不想让程序崩溃。越界了,就置空,程序还能正常运行。那么如何避免数组越界引起的程序崩溃?改写NSArray的objectAtIndex方法,然后在里面写一些自己的操作,但是我们并不知道系统的objectAtIndex是如何实现的。
整理一下思路:我们要在调用NSArray的objectAtIndex方法的时候,让它调用不是系统的objectAtIndex方法而是一个我们自己写的方法。也就是说交换方法,用到runtime。
一、首先,我们创建一个“判空”功能的NSArray的类别(关于类别,请看前面的博文),如:NSArray+JDNil.h。之后导入runtime头文件:#import "objc/runtime.h"
二、然后,我们在NSArray类的加载方法load里面交换方法。如下:
+ (void)load { [super load]; Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:)); Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(xxf_objectAtIndex:)); method_exchangeImplementations(fromMethod, toMethod); }
这三句话的意思是,获得__NSArrayI类的objectAtIndex方法和xxf_objectAtIndex方法,并把两者交换。交换之后,当调用objectAtIndex方法时,执行的是xxf_objectAtIndex里面的代码。
需要注意的有两点:
1.load方法,类的加载方法。实验证明工程启动后就会自动调用load方法,不管有没有引用头文件。最先调用,不需要引入头文件。
2.注意是__NSArrayI而不是NSArray,__NSArrayI才是真身,例如下面的数组越界崩溃信息中的[__NSArrayI objectAtIndex:]
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 3 beyond bounds [0 .. 2]'
三、实现xxf_objectAtIndex方法。如下:
- (id)xxf_objectAtIndex:(NSUInteger)index { if (self.count-1 < index) { return nil; } else { return [self xxf_objectAtIndex:index]; } }
参考博文: iOS黑魔法-Method Swizzling