使用場(chǎng)景
在我們使用系統(tǒng)的方法時(shí)枢步,功能有可能不夠用,或者在想在調(diào)用系統(tǒng)的方法時(shí)娱挨,加一些判斷余指。當(dāng)然我可以繼承系統(tǒng)的類,然后重寫該方法跷坝。但是有可能項(xiàng)目做得時(shí)間比較長(zhǎng)酵镜,一開始并沒有繼承。這時(shí)候再去繼承就會(huì)花費(fèi)一些時(shí)間柴钻。而且公司來(lái)了新人的淮韭,并不熟悉公司代碼框架的時(shí)候,有可能會(huì)忘記繼承顿颅。所以這時(shí)候我們就可以用運(yùn)行時(shí)給系統(tǒng)方法動(dòng)態(tài)添加一些代碼缸濒。
使用步驟
- 創(chuàng)建你想交換方法所在類的分類。比如你想在調(diào)用
viewWillAppear
時(shí)添加一些代碼粱腻,那么你就新建一個(gè)UIViewController
的分類庇配。 - 包含頭文件
#import <objc/runtime.h>
- 實(shí)現(xiàn)
+ (void)load{}
方法,在這個(gè)方法里面動(dòng)態(tài)交換兩個(gè)方法的地址绍些,實(shí)現(xiàn)功能捞慌。這個(gè)方法會(huì)在程序加載分類到內(nèi)存的時(shí)候就調(diào)用。
主要用到的運(yùn)行時(shí)方法
-
Method class_getClassMethod(Class cls, SEL name) cls:需要交換的類方法的所屬類 class SEL:該類方法
獲取一個(gè)類的類方法的地址 -
Method class_getInstanceMethod(Class cls, SEL name) cls:需要交換的類實(shí)例方法的所屬類 class SEL:該類的實(shí)例方法
獲取一個(gè)類的實(shí)例方法地址 -
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types) cls:被添加方法的類名 name:方法名 imp:實(shí)現(xiàn)這個(gè)方法的函數(shù) types:一個(gè)定義該函數(shù)返回值類型和參數(shù)類型的字符串
動(dòng)態(tài)的給一個(gè)類添加一個(gè)方法 -
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types) 參數(shù)如class_addMethod類似
該函數(shù)可以在運(yùn)行時(shí)動(dòng)態(tài)替換某個(gè)類的函數(shù)實(shí)現(xiàn) -
void method_exchangeImplementations(Method m1, Method m2)
交換兩個(gè)方法的地址
例子
在我們開發(fā)中使用可變數(shù)組NSMutableArray 取數(shù)組里面的元素一旦越界就會(huì)崩潰柬批。那么有什么辦法啸澡,讓線上用戶發(fā)生數(shù)組越界的時(shí)候不崩潰呢。這時(shí)候就可以用運(yùn)行時(shí)來(lái)實(shí)現(xiàn)這個(gè)功能氮帐。
新建NSMutableArray的分類嗅虏,包含runtime頭文件
// load方法會(huì)在類第一次加載到內(nèi)存的時(shí)候被調(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:);
// 兩個(gè)方法的Method
Method originalMethod = class_getInstanceMethod(cls, systemSelector);
Method swizzledMethod = class_getInstanceMethod(cls, swizzledSelector);
// 動(dòng)態(tài)添加方法
if (class_addMethod(cls, systemSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
// 添加成功的話將被交換方法的實(shí)現(xiàn)替換到這個(gè)并不存在的實(shí)現(xiàn)
class_replaceMethod(cls, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}else {
//添加不成功,交換兩個(gè)方法的實(shí)現(xiàn)
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (id)zwb_safeObjectAtIndex:(NSUInteger)index {
if (self.count > index) {
//一定是自己調(diào)用自己上沐,不會(huì)死循環(huán)皮服,因?yàn)榈刂芬呀?jīng)交換,其實(shí)調(diào)用的是系統(tǒng)的objectAtIndex
return [self zwb_safeObjectAtIndex:index];
}else {
NSLog(@"數(shù)組越界");
return nil;
}
}
當(dāng)然數(shù)組的其他方法也可以用這種方式防止崩潰参咙,大家可以試試龄广。