使用場景
在我們使用系統(tǒng)的方法時,功能有可能不夠用,或者在想在調(diào)用系統(tǒng)的方法時,加一些判斷。當然我可以繼承系統(tǒng)的類,然后重寫該方法。但是有可能項目做得時間比較長,一開始并沒有繼承。這時候再去繼承就會花費一些時間。而且公司來了新人的,并不熟悉公司代碼框架的時候,有可能會忘記繼承。所以這時候我們就可以用運行時給系統(tǒng)方法動態(tài)添加一些代碼。
使用步驟
- 創(chuàng)建你想交換方法所在類的分類。比如你想在調(diào)用
viewWillAppear
時添加一些代碼,那么你就新建一個UIViewController
的分類。 - 包含頭文件
#import <objc/runtime.h>
- 實現(xiàn)
+ (void)load{}
方法,在這個方法里面動態(tài)交換兩個方法的地址,實現(xiàn)功能。這個方法會在程序加載分類到內(nèi)存的時候就調(diào)用。
主要用到的運行時方法
-
Method class_getClassMethod(Class cls, SEL name) cls:需要交換的類方法的所屬類 class SEL:該類方法
獲取一個類的類方法的地址 -
Method class_getInstanceMethod(Class cls, SEL name) cls:需要交換的類實例方法的所屬類 class SEL:該類的實例方法
獲取一個類的實例方法地址 -
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) cls:被添加方法的類名 name:方法名 imp:實現(xiàn)這個方法的函數(shù) types:一個定義該函數(shù)返回值類型和參數(shù)類型的字符串
動態(tài)的給一個類添加一個方法 -
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) 參數(shù)如class_addMethod類似
該函數(shù)可以在運行時動態(tài)替換某個類的函數(shù)實現(xiàn) -
void method_exchangeImplementations(Method m1, Method m2)
交換兩個方法的地址
例子
在我們開發(fā)中使用可變數(shù)組NSMutableArray 取數(shù)組里面的元素一旦越界就會崩潰。那么有什么辦法,讓線上用戶發(fā)生數(shù)組越界的時候不崩潰呢。這時候就可以用運行時來實現(xiàn)這個功能。
新建NSMutableArray的分類,包含runtime頭文件
// load方法會在類第一次加載到內(nèi)存的時候被調(diào)用
+ (void)load {
//方法交換只用執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class cls = NSClassFromString(@"__NSArrayM");
// 獲取系統(tǒng)數(shù)組的selector
SEL systemSelector = @selector(objectAtIndex:);
// 自己要交換的selector
SEL swizzledSelector = @selector(zwb_safeObjectAtIndex:);
// 兩個方法的Method
Method originalMethod = class_getInstanceMethod(cls, systemSelector);
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector);
// 動態(tài)添加方法
if (class_addMethod(cls, systemSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
// 添加成功的話將被交換方法的實現(xiàn)替換到這個并不存在的實現(xiàn)
class_replaceMethod(cls, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else {
//添加不成功,交換兩個方法的實現(xiàn)
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (id)zwb_safeObjectAtIndex:(NSUInteger)index {
if (self.count > index) {
//一定是自己調(diào)用自己,不會死循環(huán),因為地址已經(jīng)交換,其實調(diào)用的是系統(tǒng)的objectAtIndex
return [self zwb_safeObjectAtIndex:index];
}else {
NSLog(@"數(shù)組越界");
return nil;
}
}
當然數(shù)組的其他方法也可以用這種方式防止崩潰,大家可以試試。