什么是method swizzling?

Method Swizzling(方法交換),顧名思義,就是將兩個方法的實現交換,即由原來的A-AImp、B-BImp對應關系變成了A-BImp、B-AImp。

那為什么無緣無故要將兩個方法的實現交換呢?

1、hook:在開發中,經常用到系統提供的API,但出于某些需求,我們可能會對某些方法的實現不太滿意,就想去修改它以達到更好的效果,Hook由此誕生。iOS開發會使用Method Swizzling來達到這樣的效果:當特定的消息發出時,會先到達我們提前預置的消息處理函數,取得控制權來加工消息以及后續處理。

2、面向切面編程:實際上,要改變一個方法的實現有幾種方法,比如繼承重寫、分類重寫等等,但在開發中,往往由于業務需要需要在代碼中添加一些瑣碎的、跟主要業務邏輯無關的東西,使用這兩者都有局限性,使用方法交換動態給指定的方法添加代碼以達到解耦的效果。

Method Swizzling原理

每個類都維護一個方法(Method)列表,Method則包含SEL和其對應IMP的信息,方法交換做的事情就是把SEL和IMP的對應關系斷開,并和新的IMP生成對應關系。(IMP有點類似函數指針,指向具體的Method實現)

交換前:Asel->AImp Bsel->BImp
交換后:Asel->BImp Bsel->AImp

Method Swizzling相關函數介紹

//獲取通過SEL獲取一個方法
class_getInstanceMethod

//獲取一個方法的實現
method_getImplementation

//獲取一個OC實現的編碼類型
method_getTypeEncoding

//給方法添加實現
class_addMethod

//用一個方法的實現替換另一個方法的實現
class_replaceMethod

//交換兩個方法的實現
method_exchangeImplementations

Method Swizzling實現的過程

static dispatch_once_t onceToken; 
dispatch_once(&onceToken, ^{ 
    //case1: 替換實例方法 
    Class selfClass = [self class]; 
    //case2: 替換類方法 
    Class selfClass = object_getClass([self class]); 

    //源方法的SEL和Method 
    SEL oriSEL = @selector(viewWillAppear:); 
    Method oriMethod = class_getInstanceMethod(selfClass, oriSEL); 

    //交換方法的SEL和Method 
    SEL cusSEL = @selector(customViewWillApper:); 
    Method cusMethod = class_getInstanceMethod(selfClass, cusSEL); 

    //先嘗試給源方法添加實現,這里是為了避免源方法沒有實現的情況 
    BOOL addSucc = class_addMethod(selfClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod)); 
    if (addSucc) { 
        //添加成功:將源方法的實現替換到交換方法的實現 
        class_replaceMethod(selfClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod)); 
    }else { 
        //添加失敗:說明源方法已經有實現,直接將兩個方法的實現交換即可 
        method_exchangeImplementations(oriMethod, cusMethod); 
    } 
});

Method Swizzling注意事項

1、方法交換應該保證唯一性和原子性
唯一性:應該盡可能在+load方法中實現,這樣可以保證方法一定會調用且不會出現異常。
原子性:使用dispatch_once來執行方法交換,這樣可以保證只運行一次。

2、一定要調用原始實現
由于iOS的內部實現對我們來說是不可見的,使用方法交換可能會導致其代碼結構改變,而對系統產生其他影響,因此應該調用原始實現來保證內部操作的正常運行。

3、方法名必須不能產生沖突
這個是常識,避免跟其他庫產生沖突。

4、做好記錄
記錄好被影響過的方法,不然時間長了或者其他人debug代碼時候可能會對一些輸出信息感到困惑。

5、如果非迫不得已,盡量少用方法交換
雖然方法交換可以讓我們高效地解決問題,但是如果處理不好,可能會導致一些莫名其妙的bug。

相關使用例子見下面的鏈接~

整理相關鏈接:http://www.lxweimin.com/p/3efc3e94b14c

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容