在Swift3.1 之前,Method Swizzling的實(shí)現(xiàn)都放在了initialize()方法中,即
open override class func initialize() { // Method Swizzling }
我們?cè)?initialize()中實(shí)現(xiàn)Method Swizzling,是因?yàn)檫@個(gè)方法是每一個(gè)Class都必須實(shí)現(xiàn)且一定會(huì)執(zhí)行的方法,它就相當(dāng)于給我們提供了一個(gè)入口,允許我們將runtime代碼插入到適當(dāng)?shù)奈恢谩?br>
但是在3.1之后,蘋果官方已經(jīng)不建議在 Swift中 override initialize Method ,如果你嘗試override,Xcode會(huì)提示: 'initialize()' defines Objective-C class method 'initialize', which is not guaranteed to be invoked by Swift and will be disallowed in future versions
之前喵神有在他的書的第二版中提到過(guò)利用 override initialize 來(lái)做 Method Swizzling , 但在第三版更新的時(shí)候刪除了這部分內(nèi)容,因?yàn)檫魃裼X得應(yīng)該用更加 Swift 化的方法來(lái)做。
那么,如果,我們非要Method Swizzling,現(xiàn)在該怎么做呢?
國(guó)外的大牛已經(jīng)提供了一種優(yōu)雅的解決方案
解決思路:我們可以利用代理模式在程序剛啟動(dòng)時(shí)使用runtime獲取所有的Class,然后對(duì)它們遍歷,如果它是Protocol的代理就立即執(zhí)行代理方法,我們只需要在代理方法中實(shí)現(xiàn)Method Swizzling代碼。
第一步:
第二步:
第三步:
完成了上述三步之后,寫兩個(gè)ViewController,第一個(gè)Controller中override viewWillDisappear, 第二個(gè)Controller中override viewWillAppear, viewDidAppear。編譯運(yùn)行,從第一個(gè)Controller push 到第二個(gè)Controller的輸出結(jié)果為
可以看到Method Swizzling已經(jīng)成功。
后記
我個(gè)人認(rèn)為,目前在代碼中需要使用runtime大多和UIKit相關(guān),而UIKit又是由OC編寫的,事實(shí)上使用OC來(lái)swizzle是最好的方法,強(qiáng)行使用Swift豈不是緣木求魚?既然蘋果都說(shuō)了 swizzle 只能用于 OC ,那就把這部分東西寫在 OC 文件里面,然后 Bridge 引用,避免以后 因Swift 語(yǔ)法變化而造成不便。
.p.s 細(xì)心的讀者可能發(fā)現(xiàn)了我在代理中定義方法用的是static,而在類中實(shí)現(xiàn)的時(shí)候替換成了class,為什么這么做呢?
因?yàn)閟taitc是無(wú)法被override的,如果你在UINavigationController中需要實(shí)現(xiàn)awake(),而你之前已經(jīng)在UIViewController中實(shí)現(xiàn)了awake(),此時(shí)你實(shí)現(xiàn)awake()就會(huì)被要求override(因?yàn)閁INavigationController繼承自UIViewController),而static方法是無(wú)法override的。喵神曾在他的書中對(duì)class和static進(jìn)行比較:在類中class和static的作用是一樣的。所以,在UIViewController中我用class替換了static。
參考鏈接
Swift 3.1 deprecates initialize(). How can I achieve the same thing?