
此文是在讀者已經(jīng)對(duì) Method Swizzling 的使用和原理有一定的了解和基礎(chǔ)上對(duì)網(wǎng)絡(luò)中普遍的 Method Swizzling 方案進(jìn)行解讀。
方案一:
Method ori_Method = class_getInstanceMethod([NSArray class], @selector(lastObject));
Method my_Method = class_getInstanceMethod([NSArray class], @selector(myLastObject));
method_exchangeImplementations(ori_Method, my_Method);
方案二:
Class class = [self class];
// When swizzling a class method, use the following:
// Class class = object_getClass((id)self);
SEL originalSelector = @selector(viewWillAppear:);
SEL swizzledSelector = @selector(xxx_viewWillAppear:);
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);
}
});
方案一我想大家必定都很熟悉,至于方案二也在不少相關(guān)文章中有提及,不過(guò)方案二中這一大坨的判斷是干嘛的?我交換個(gè)方法有必要那么復(fù)雜嗎/(ㄒoㄒ)/~~,而且這些判斷里的方法調(diào)用和參數(shù)又是什么鬼?看不明白啊啊啊啊啊啊啊啊啊啊,不用擔(dān)心接下來(lái)博主將為大家解讀方案二的實(shí)現(xiàn)原委??
首先來(lái)看看這行代碼
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
在方法交換之前,我們首先為要交換的類(lèi) class 添加一個(gè)名為 originalSelector 實(shí)現(xiàn)內(nèi)容與結(jié)構(gòu)體 swizzledMethod 中一致的方法,這一步主要是為了防止方法交換時(shí),交換到由父類(lèi)中所繼承的方法,原本你只是想交換具體子類(lèi)中的方法實(shí)現(xiàn)卻把原有父類(lèi)的方法實(shí)現(xiàn)給替換了,這就造成了原本調(diào)用父類(lèi)方法時(shí)的結(jié)果異常。
1.首先創(chuàng)建一個(gè)父類(lèi) Father 并在內(nèi)部實(shí)現(xiàn)一個(gè)名為 test 的方法
Father.h
#import <Foundation/Foundation.h>
@interface Father : NSObject
- (void)test;
@end
Father.m
#import "Father.h"
@implementation Father
- (void)test {
NSLog(@"Father test");
}
@end
2.創(chuàng)建一個(gè) Father 的子類(lèi) Son,不做任何操作
Son.h
#import "Father.h"
@interface Son : Father
@end
Son.m
#import "Son.h"
@implementation Son
@end
Son+Swizzling.m
#import "Son+Swizzling.h"
#import <objc/runtime.h>
@implementation Son (Swizzling)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL oriSEL = @selector(test);
SEL swiSEL = @selector(swi_test);
Method oriMethod = class_getInstanceMethod(class, oriSEL);
Method swiMethod = class_getInstanceMethod(class, swiSEL);
method_exchangeImplementations(oriMethod, swiMethod);
});
}
- (void)swi_test {
// [self swi_test];
NSLog(@"Son swi_test");
}
@end
代碼貼完了,現(xiàn)在讓我們看看在此種情況下沒(méi)有第一步的預(yù)防措施會(huì)造成怎么樣的結(jié)果

我們?cè)谡{(diào)用的 Father 的 test 方法時(shí)的打印結(jié)果和Son一致,同時(shí)這也證明了上面的猜想是正確的。
為什么會(huì)造成這樣的結(jié)果呢?因?yàn)樵摵瘮?shù)所檢索得到的Method是從當(dāng)前類(lèi)及其父類(lèi)中進(jìn)行自下而上的查找,所以直接進(jìn)行方法替換可能會(huì)造成父類(lèi)原本實(shí)現(xiàn)被替換也就能解釋了
Method oriMethod = class_getInstanceMethod(class, oriSEL);
我們繼續(xù)往下看
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
這里的判斷大意:如果方法添加成功,則說(shuō)明原類(lèi)并不存在將要被替換的 originalMethod 或者父類(lèi)已存在同名方法,此時(shí)我們?cè)賹?duì)名為 swizzledSelector方法進(jìn)行整體結(jié)構(gòu)替換,這樣一來(lái)我們變向?qū)崿F(xiàn)了方法交換,你在調(diào)用原類(lèi) originalSelector 時(shí)的實(shí)現(xiàn)為 swizzledMethod 結(jié)構(gòu)中的具體內(nèi)容,而原本用來(lái)交換的方法 swizzledSelector 的實(shí)現(xiàn)也變成了 originalMethod 的具體實(shí)現(xiàn)。如果方法添加失敗,則說(shuō)明原類(lèi)中已經(jīng)存在了要被替換的 originalSelector,可以安全的進(jìn)行 method_exchangeImplementations 交換。