面试时曾被问到一个问题,如何使用runtime防止数组越界?当时想到的是用分类重写系统的ObjectAtIndex:方法就好了,何必多此一举,后来在学习runtime中认识到,使用runtime是一种更好的方式。
添加数组的分类,.h文件不需要操作。.m文件如下:
#import "NSArray+CPFSafe.h"
#import <objc/runtime.h>
@implementation NSArray (CPFSafe)
+ (void)load
{
//方法交换应该保证在程序中只执行一次。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method origin_method = class_getInstanceMethod([NSArray class], @selector(objectAtIndex:));
Method new_method = class_getInstanceMethod([self class], @selector(safeObjectAtIndex:));
method_exchangeImplementations(origin_method, new_method);
// 对于数组 arr[10] 这种形式的使用,OC会调用objectAtIndexedSubscript:方法
Method sub_origin_method = class_getInstanceMethod([NSArray class], @selector(objectAtIndexedSubscript:));
Method sub_new_method = class_getInstanceMethod([self class], @selector(safeObjectAtIndexedSubscript:));
method_exchangeImplementations(sub_origin_method, sub_new_method);
});
}
-(NSObject *)safeObjectAtIndex:(NSUInteger)index
{
if (index > self.count -1)
{
return nil;
}else
{
//不会产生递归调用,还记得我们方法的实现交换了吗
return [self safeObjectAtIndex:index];
}
}
- (NSObject *)safeObjectAtIndexedSubscript:(NSUInteger)index
{
if (index > self.count -1)
{
return nil;
}else
{
return [self safeObjectAtIndexedSubscript:index];
}
}
@end
使用runtime 函数class_getInstanceMethod(Class,@selecter)实时获取方法,然后使用method_exchangeImplementations函数交换自己添加的方法和系统方法的实现。
自己的方法里对越界做一些处理。
好了,检验一下
调用的地方需要引入头文件
#import "NSArray+CPFSafe.h"
NSArray *arr = @[@"0",@"new"];
NSObject *obj = arr[1];
NSObject *obj2 = arr[3];
NSLog(@"obj:%@",obj);
NSLog(@"obj:%@",obj2);
打印结果:
2018-10-30 16:21:48.126 runtime[1550:124093] obj:new
2018-10-30 16:21:48.127 runtime[1550:124093] obj:(null)
不会发生崩溃错误。