面試時(shí)曾被問(wèn)到一個(gè)問(wèn)題,如何使用runtime防止數(shù)組越界?當(dāng)時(shí)想到的是用分類重寫系統(tǒng)的ObjectAtIndex:方法就好了,何必多此一舉,后來(lái)在學(xué)習(xí)runtime中認(rèn)識(shí)到,使用runtime是一種更好的方式。
添加數(shù)組的分類,.h文件不需要操作。.m文件如下:
#import "NSArray+CPFSafe.h"
#import <objc/runtime.h>
@implementation NSArray (CPFSafe)
+ (void)load
{
? ? //方法交換應(yīng)該保證在程序中只執(zhí)行一次。
? ? 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);
? ? ? ? // 對(duì)于數(shù)組 arr[10] 這種形式的使用,OC會(huì)調(diào)用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
? ? {
? ? ? ? //不會(huì)產(chǎn)生遞歸調(diào)用,還記得我們方法的實(shí)現(xiàn)交換了嗎
? ? ? ? return [self safeObjectAtIndex:index];
? ? }
}
- (NSObject *)safeObjectAtIndexedSubscript:(NSUInteger)index
{
? ? if (index > self.count -1)
? ? {
? ? ? ? return nil;
? ? }else
? ? {
? ? ? ? return [self safeObjectAtIndexedSubscript:index];
? ? }
}
@end
使用runtime 函數(shù)class_getInstanceMethod(Class,@selecter)實(shí)時(shí)獲取方法,然后使用method_exchangeImplementations函數(shù)交換自己添加的方法和系統(tǒng)方法的實(shí)現(xiàn)。
自己的方法里對(duì)越界做一些處理。
好了,檢驗(yàn)一下
調(diào)用的地方需要引入頭文件
#import "NSArray+CPFSafe.h"
NSArray *arr = @[@"0",@"new"];
? ? NSObject *obj = arr[1];
? ? NSObject *obj2 = arr[3];
? ? NSLog(@"obj:%@",obj);
? ? NSLog(@"obj:%@",obj2);
打印結(jié)果:
2018-10-30 16:21:48.126 runtime[1550:124093] obj:new
2018-10-30 16:21:48.127 runtime[1550:124093] obj:(null)
不會(huì)發(fā)生崩潰錯(cuò)誤。