更新
針對本文和評論區(qū)的大家的疑惑,我統(tǒng)一寫到了這篇文章中,解釋了class_addMethod ,class_replaceMethod和method_exchangeImplementations這三個方法的作用和為什么要用它們,大家可以看下:
《詳講Runtime方法交換(class_addMethod ,class_replaceMethod和method_exchangeImplementations)》
前話
這幾天在系統(tǒng)的學(xué)習(xí) runtime,在學(xué)習(xí) runtime 的基礎(chǔ)使用案例中,"方法替換"這種使用情況下,發(fā)現(xiàn)有兩種寫法. 其實也不是兩種寫法,準確的來說一種是比較嚴謹?shù)?另一種則沒有那么嚴謹.
發(fā)現(xiàn)這兩種寫法的差異后,我主要集中在下列:
class_addMethodclass_replaceMethodmethod_exchangeImplementations
哪個方法的具體作用.
下面,這篇文章就這兩種寫法和上述三種方法的區(qū)別.
第一種寫法
在《OC最實用的runtime總結(jié),面試、工作你看我就足夠了!》的時候,它里邊的寫法是簡單的獲取到被替換和替換方法的Method.然后直接使用method_exchangeImplementations進行方法的替換. 最開始使用的時候,因為測試范例比較簡單,所以并沒有發(fā)現(xiàn)這樣寫的弊端.但是確實能夠?qū)崿F(xiàn)方法替換的效果. 代碼如下:
+(void)load{
//獲取兩個類的方法
Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method m2 = class_getClassMethod([UIImage class], @selector(ll_imageName:));
//開始交換方法實現(xiàn)
method_exchangeImplementations(m1, m2);
}
在后來看到《runtime詳解》的時候,發(fā)現(xiàn)作者的寫法并不是這樣,雖然作者添加少量注釋,但是愚鈍的我還沒有想清楚,這也是這篇文章的初衷,也是下一小結(jié)的由來.
第二種寫法
上一節(jié)的這種情況雖然能夠?qū)崿F(xiàn)我們想要的效果.但是我們有沒有想過這種情況:
" 周全起見,有兩種情況要考慮一下。第一種情況是要復(fù)寫的方法(overridden)并沒有在目標類中實現(xiàn)(notimplemented),而是在其父類中實現(xiàn)了。第二種情況是這個方法已經(jīng)存在于目標類中(does existin the class itself)。這兩種情況要區(qū)別對待。 (譯注: 這個地方有點要明確一下,它的目的是為了使用一個重寫的方法替換掉原來的方法。但重寫的方法可能是在父類中重寫的,也可能是在子類中重寫的。) 對于第一種情況,應(yīng)當(dāng)先在目標類增加一個新的實現(xiàn)方法(override),然后將復(fù)寫的方法替換為原先(的實現(xiàn)(original one)。 對于第二情況(在目標類重寫的方法)。這時可以通過method_exchangeImplementations來完成交換."
---- 以上來自:《Objective-C的方法替換》
+(void)load{
NSString *className = NSStringFromClass(self.class);
NSLog(@"classname %@", className);
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//要特別注意你替換的方法到底是哪個性質(zhì)的方法
// When swizzling a Instance method, use the following:
// Class class = [self class];
// When swizzling a class method, use the following:
Class class = object_getClass((id)self);
SEL originalSelector = @selector(systemMethod_PrintLog);
SEL swizzledSelector = @selector(ll_imageName);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
解析:
上面提到的:
dispatch_once這里不是“單例”,是保證方法替換只執(zhí)行一次.
說明:
systemMethod_PrintLog:被替換方法ll_imageName:替換方法
class_addMethod:如果發(fā)現(xiàn)方法已經(jīng)存在,會失敗返回,也可以用來做檢查用,我們這里是為了避免源方法沒有實現(xiàn)的情況;如果方法沒有存在,我們則先嘗試添加被替換的方法的實現(xiàn)
1.如果返回成功:則說明被替換方法沒有存在.也就是被替換的方法沒有被實現(xiàn),我們需要先把這個方法實現(xiàn),然后再執(zhí)行我們想要的效果,用我們自定義的方法去替換被替換的方法. 這里使用到的是class_replaceMethod這個方法. class_replaceMethod本身會嘗試調(diào)用class_addMethod和method_setImplementation,所以直接調(diào)用class_replaceMethod就可以了)
2.如果返回失敗:則說明被替換方法已經(jīng)存在.直接將兩個方法的實現(xiàn)交換即
另外:
- 我們可以利用
method_exchangeImplementations來交換2個方法中的IMP - 我們可以利用
class_replaceMethod來修改類 - 我們可以利用
method_setImplementation來直接設(shè)置某個方法的IMP
其實我們?nèi)绻?研究過 AFN 代碼的話,會發(fā)現(xiàn), AFN 就是第二種寫法.在AFURLSessionManager.m的第296行:
static inline void af_swizzleSelector(Class class, SEL originalSelector, SEL swizzledSelector) {
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
if (class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
詳盡的代碼請查看 Demo.
下載地址
具體的 Demo 代碼可以在我的 GitHub 上找到 Demo地址
其它
關(guān)于 load 的調(diào)用次數(shù)問題,大家可以查看這兩篇文章.+(void)load和+(void)initialize可當(dāng)做普通類方法(Class Method)調(diào)用的.《NSObject的load和initialize方法!》和《Objective C類方法load和initialize的區(qū)別》
參考文章
交流

希望能和大家交流技術(shù)
我的博客地址: http://www.lilongcnc.cc/