Swift3中的 Method Swizzling


先聊聊Method Swizzling

從 Objective-C 開始, runtime一直是解決坑爹需求和面試裝逼的一大利器,然而聊起 runtime 很多人都第一個(gè)想到 Method Swizzling,.因?yàn)?Objective-C中調(diào)用方法都是動(dòng)態(tài)實(shí)現(xiàn)的,當(dāng)運(yùn)行時(shí)的才確定到底執(zhí)行哪個(gè)方法,而 Method Swizzling 就是利用這個(gè)特點(diǎn)來解決很多問題.

現(xiàn)在關(guān)于 Runtime 和 Method Swizzling 的文章太多了,我推薦一篇:
神經(jīng)病院Objective-C Runtime出院第三天——如何正確使用Runtime
@一縷殤流化隱半邊冰霜 大神寫的關(guān)于 runtime這幾篇,看完基本對 runtime 就沒什么問題了吧...

再看看 Swift3.0中的 Method Swizzling

先來看看 swizzling 在 Objective-C 中的注意點(diǎn):(對比上文鏈接中)

1.Swizzling應(yīng)該總在category的 +load中執(zhí)行 ( Objective-C )

那在 Swift 中, extension 并不是運(yùn)行時(shí)加載的, 因此也沒有加載時(shí)候就會(huì)被調(diào)用的類似 +load 的方法. 事實(shí)上,Swift 實(shí)現(xiàn)的 load 并不是在 app 運(yùn)行開始就被調(diào)用的。基于這些理由,我們使用另一個(gè)類初始化時(shí)會(huì)被調(diào)用的方法來進(jìn)行交換:

open override static func initialize() {
    // Method Swizzling
}

這一條部分來自喵神 swiift tips 第二版, 喵神在第三版中刪除了 swizzling 這個(gè)章節(jié),理由是這部分更多是 Objective-C的內(nèi)容. 我個(gè)人覺得如果在 Swift 中還需要用 Swizzling 這種技術(shù)來實(shí)現(xiàn)需求, 不如用更 Swifty的方式去解決問題, 函數(shù)式或者面向協(xié)議等等等??

2.Swizzling應(yīng)該總是在dispatch_once中執(zhí)行

那么,問題來了,在3.0版本 dispatch once 已經(jīng)被廢棄,這怎么辦?

剛巧的是前幾天群里的老司機(jī) @沒故事的卓同學(xué) 寫了篇 [譯]Swift 3 中實(shí)現(xiàn)Dispatch once擴(kuò)展

通過給DispatchQueue實(shí)現(xiàn)一個(gè)擴(kuò)展方法來實(shí)現(xiàn) Dispatch once.
至于沒什么要 dispatch_once呢? 因?yàn)?Swizzling會(huì)改變?nèi)譅顟B(tài),所以用dispatch_once來確保無論多少線程都只會(huì)被執(zhí)行一次.

3. Swift自定義類中使用 Method Swizzling

因?yàn)镸ethod Swizzling的實(shí)現(xiàn)是基于 Objective-C 的動(dòng)態(tài)派發(fā)機(jī)制,所以有兩條限制
1.包含 swizzle 方法的類需要繼承自 NSObject
2.如果要 Swizzle 的是 Swift 類型的方法的話,需要將原方法和替換方法都加上 dynamic 標(biāo)記,以指明它們需要使用動(dòng)態(tài)派發(fā)機(jī)制


上個(gè) sample:

extension UIViewController {
    open override static func initialize() {
        struct Static {
            static var token = NSUUID().uuidString
        }

        if self != UIViewController.self {
            return
        }

        DispatchQueue.once(token: Static.token) { 
            let originalSelector = #selector(UIViewController.viewWillAppear(_:))
            let swizzledSelector = #selector(UIViewController.xl_viewWillAppear(animated:))

            let originalMethod = class_getInstanceMethod(self, originalSelector)
            let swizzledMethod = class_getInstanceMethod(self, swizzledSelector)

            
            //在進(jìn)行 Swizzling 的時(shí)候,需要用 class_addMethod 先進(jìn)行判斷一下原有類中是否有要替換方法的實(shí)現(xiàn)
            let didAddMethod: Bool = class_addMethod(self, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))
            //如果 class_addMethod 返回 yes,說明當(dāng)前類中沒有要替換方法的實(shí)現(xiàn),所以需要在父類中查找,這時(shí)候就用到 method_getImplemetation 去獲取 class_getInstanceMethod 里面的方法實(shí)現(xiàn),然后再進(jìn)行 class_replaceMethod 來實(shí)現(xiàn) Swizzing

            if didAddMethod {
                class_replaceMethod(self, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
            } else {
                method_exchangeImplementations(originalMethod, swizzledMethod)
            }
        }
    }

    func xl_viewWillAppear(animated: Bool) {
        self.xl_viewWillAppear(animated: animated)
        print("xl_viewWillAppear in swizzleMethod")
    }
}

extension DispatchQueue {
    private static var onceTracker = [String]()

    open class func once(token: String, block:() -> Void) {
        objc_sync_enter(self)
        defer { objc_sync_exit(self) }

        if onceTracker.contains(token) {
            return
        }
        
        onceTracker.append(token)
        block()
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(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評論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,872評論 33 466
  • 轉(zhuǎn)載:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麥子閱讀 827評論 0 2
  • 本文詳細(xì)整理了 Cocoa 的 Runtime 系統(tǒng)的知識(shí),它使得 Objective-C 如虎添翼,具備了靈活的...
    lylaut閱讀 864評論 0 4
  • 1.因?yàn)槟?我知道石頭剪子布的真諦 2.不喜歡背對背的擁抱.但連牽手都是一種奢侈的欲望。 3.再次來到我們一起走過...
    釦木閱讀 288評論 0 2

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