Method Swizzling中的那點(diǎn)事

1386EEBF1D7C17C4C7B3E10690402A66.jpg

此文是在讀者已經(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é)果

屏幕快照 2019-02-07 下午9.16.57.png

我們?cè)谡{(diào)用的 Fathertest 方法時(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 交換。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,030評(píng)論 0 9
  • 轉(zhuǎn)載:http://www.cocoachina.com/ios/20161102/17920.html 因?yàn)镺b...
    F麥子閱讀 699評(píng)論 0 1
  • 目錄 Objective-C Runtime到底是什么 Objective-C的元素認(rèn)知 Runtime詳解 應(yīng)用...
    Ryan___閱讀 2,008評(píng)論 1 3
  • runtime 和 runloop 作為一個(gè)程序員進(jìn)階是必須的,也是非常重要的, 在面試過(guò)程中是經(jīng)常會(huì)被問(wèn)到的, ...
    made_China閱讀 1,269評(píng)論 0 7
  • 我們常常會(huì)聽(tīng)說(shuō) Objective-C 是一門(mén)動(dòng)態(tài)語(yǔ)言,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,319評(píng)論 0 7

友情鏈接更多精彩內(nèi)容