前言
記得《大話西游2》中有這么個橋段楷怒,紫霞仙子和豬八戒中招移魂幻影大法后靈魂互換蛋勺,當時看的也是淚流滿面。鸠删。而Method Swizzling就是傳說中的移魂幻影大法抱完!身體還是那個身體,但是靈魂已被互換刃泡,同樣的身體言行舉止都變成被換對象巧娱。
正文
代碼很好理解碉怔,如果還在想為什么不會陷入死循環(huán),是時候重溫這部經(jīng)典影片了禁添。
如果交換后再交換呢撮胧?
顯然,偶數(shù)次交換后方法的實現(xiàn)不變
說一下使用Method Swizzling的幾個注意點:
- 最好在
+ (void)load
中實現(xiàn)交換
通常我們使用這個技巧時老翘,希望在整個項目都可以實現(xiàn)交換效果芹啥,毫無疑問+ (void)load
是最佳選擇。只要當前類參與編譯铺峭,在程序啟動后就會調(diào)用墓怀,并且僅調(diào)用一次,這樣就防止了IMP
出現(xiàn)偶數(shù)次交換的情況卫键。在繼承關(guān)系下傀履,先調(diào)用父類的+ (void)load
再調(diào)子類,繼承關(guān)系下的分類調(diào)用關(guān)系與Compile Source
的順序相關(guān)永罚。
既然+ (void)load
只會調(diào)用一次,為什么還要加dispatch_once
卧秘?
-
dispatch_once
如果你剛好知道+ (void)initialize
的存在呢袱,可能會覺得在+ (void)initialize
中實現(xiàn)交換也未嘗不可,畢竟當外界調(diào)用當前類的方法時翅敌,會提前調(diào)用initialize羞福,好像也是只調(diào)一次。
首先要糾正一個錯誤:initialize并非只會調(diào)用一次蚯涮,如調(diào)用子類的某個方法治专,并且只實現(xiàn)父類的initialize,這個時候父類的initalize會調(diào)用2次遭顶,所以加上dispatch_once
可以確保交換代碼只執(zhí)行一次张峰。
除此之外,swizzle寫在initialize中當遇到并發(fā)情況時棒旗,程序仍然有可能詭異的crash掉喘批,所以要加dispatch_once
。
如果swizzle寫在+ (void)load
是不是就不用加dispatch_once
了铣揉,因為不涉及并發(fā)饶深,而且+ (void)load
不是只會調(diào)用一次么?load是系統(tǒng)在調(diào)逛拱,但同樣支持手動調(diào)用敌厘,為防止特殊情況發(fā)生,加dispatch_once
總沒錯朽合。
- swizzle多為交換系統(tǒng)自帶方法(下面兩條普遍適用)
- 在交換時要調(diào)用系統(tǒng)方法的原始實現(xiàn)俱两,即調(diào)用super
- 為避免出現(xiàn)命名與系統(tǒng)某自帶方法相同饱狂,方法名最好帶上前綴
寫在最后
更好的寫法是,在load的dispatch_once內(nèi)部末尾加上這段代碼
讓我們先來搞清楚這段代碼究竟是在做什么:
第一句是添加IMP
swizzlingMethod到SEL
originSel中(只是添加锋华,不會覆蓋orginSel的原有實現(xiàn))嗡官,當且僅當originSel
為當前類的父類方法時才能添加成功。如果添加成功將SEL
swizzlingSel的實現(xiàn)用原有的IMP
originMethod覆蓋毯焕,關(guān)系如下:
這是一種變相的swizzle衍腥。