日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Method Swizzling 为什么要先调用 class_addMethod?

發布時間:2025/3/18 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Method Swizzling 为什么要先调用 class_addMethod? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

先上個 Swift 中的 demo:Method Swizzling

Swift 中的實現

其實 Swift 中實現原理和 OC 基本一致,只是蘋果爸爸不再允許在 Swift 中使用+load()和+initialize()方法,這當然難不倒各種大神,那么我就做次農夫山泉。。。

Swizzling

先抽取 swizzling 的實現到NSObject的擴展當中:

extension NSObject {static func swizzlingForClass(_ forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {guard let originalMethod = class_getInstanceMethod(forClass, originalSelector),let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) else {return}let isAddSuccess = class_addMethod(forClass, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))if isAddSuccess {class_replaceMethod(forClass, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))} else {method_exchangeImplementations(originalMethod, swizzledMethod)}} } 復制代碼

可以看到核心實現和 OC 是完全一致的,那么剩下的就是模擬 OC 版本實現中的+load()和dispatch_once。

dispatch_once

我們用viewDidLoad來做個dispatch_once的示范:

extension UIViewController {static func swizzleViewDidLoad() {_ = self.swizzleMethod}@objc func swizzled_viewDidLoad() {swizzled_viewDidLoad()print("嘻嘻")}private static let swizzleMethod: Void = {let originalSelector = #selector(viewDidLoad)let swizzledSelector = #selector(swizzled_viewDidLoad)swizzlingForClass(UIViewController.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)}() } 復制代碼

在 Swift 中static let這樣聲明的變量其實已經用到dispatch_once,而且static自帶lazy屬性,要在封裝函數swizzleViewDidLoad被調用時候才調用。

+load()

OC 中+load()方法會在類被裝載時調用,確保需要用到的方法都是被 Swizzling 過的。Swift 中可以在AppDelegate 的init方法中手動調用 swizzle 方法模擬+load()實現。

class AppDelegate: UIResponder, UIApplicationDelegate {override init() {super.init()UIViewController.swizzleViewDidLoad()} } 復制代碼

為什么要先調用 class_addMethod?

class_addMethod這個方法是很容易被人忽視的,對于 Swizzling 一節中的代碼,還有一種常見的寫法:

extension NSObject {static func swizzlingForClass(_ forClass: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {guard let originalMethod = class_getInstanceMethod(forClass, originalSelector),let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector) else {return}method_exchangeImplementations(originalMethod, swizzledMethod)} } 復制代碼

這種方式就只是簡單的直接交換了originalMethod和swizzledMethod。乍一看貌似沒有問題(其實最開始我絞盡腦汁也沒想清楚到底哪里不對。。。),但是為什么各路大神都是用的第一種方式呢?網上有種說法:

要先嘗試添加原 selector 是為了做一層保護,因為如果這個類沒有實現原始方法"originalSel" ,但其父類實現了,那 class_getInstanceMethod 會返回父類的方法。這樣 method_exchangeImplementations 替換的是父類的那個方法,這當然不是你想要的。所以我們先嘗試添加 originalSel ,如果已經存在,再用 method_exchangeImplementations 把原方法的實現跟新的方法實現給交換掉。

其實這種說法已經算是比較明確問題所在了,但是愚笨的我還是沒有想通到底為什就“這當然不是你想要的”了呢。

又是一番絞盡腦汁。。。終于 Biuer 的一下想通了

在舉栗子前引用一段對 Selectors、Methods 和 Implementations 理解:

理解 selector, method, implementation 這三個概念之間關系的最好方式是:在運行時,類(Class)維護了一個消息分發列表來解決消息的正確發送。每一個消息列表的入口是一個方法(Method),這個方法映射了一對鍵值對,其中鍵值是這個方法的名字 selector(SEL),值是指向這個方法實現的函數指針 implementation(IMP)。 Method swizzling 修改了類的消息分發列表使得已經存在的 selector 映射了另一個實現 implementation,同時重命名了原生方法的實現為一個新的 selector。

假設父類有個方法method,子類未重寫method方法,子類的中想要拿來替換的方法為swizzledMethod。

  • 用第二種方式進行方法交換

    • 在子類的實例中調用method方法時,確實按預期正常運行的
    • 在父類的實例中調用method方法時,就開始崩潰了。因為方法交換后,method方法的IMP其實和子類swizzledMethod的IMP進行了交換,此時等同于父類調用子類方法,當然會崩潰。
  • 用第一種方式進行方法交換

    class_addMethod先判斷了子類中是否有method方法

    • 如果有,則添加失敗,直接進行交換
    • 如果沒有,則添加成功,將swizzledMethod的IMP賦值給method這個Selector,然后在將method的IMP(其實是父類中的實現)賦值給swizzledMethod這個Selector
  • 轉載于:https://juejin.im/post/5cb6df44e51d456e6f45c6f1

    總結

    以上是生活随笔為你收集整理的Method Swizzling 为什么要先调用 class_addMethod?的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。