使用Runtime替換實(shí)例方法和類方法實(shí)現(xiàn)

方案1.通過動(dòng)態(tài)創(chuàng)建類和實(shí)現(xiàn)其中的方法,然后把原對(duì)象的類型修改成你創(chuàng)建的對(duì)象通過消息派發(fā)使對(duì)象調(diào)用你新實(shí)現(xiàn)的方法

extension NSObject {
 //動(dòng)態(tài)創(chuàng)建類并重寫方法
   func dynamicCreatClass(selector: Selector, action: (() -> ())?) {
       // 創(chuàng)建類的類名
       let classFullName = "ATDynamic_\(self.classForCoder.description()))"
       // 獲取原來類
       let originalClass = type(of: self)
       
       // 判斷這個(gè)類是否已經(jīng)存在
       if let dynamicClass = objc_allocateClassPair(originalClass, classFullName, 0) {
           // 動(dòng)態(tài)的創(chuàng)建這個(gè)類
           objc_registerClassPair(dynamicClass)
           // 將原對(duì)象修改成新的類型
           object_setClass(self, dynamicClass)
           
           // 實(shí)現(xiàn)新的方法
           let printName: @convention(block) (Any?) -> () = { nullSelf in
               guard let _ = nullSelf else { return }
               // 獲取原來類中方法的Imp
               let originalImp = class_getMethodImplementation(originalClass, selector)
               // 定義一個(gè)方法類型與msgSend的參數(shù)類似 第一個(gè)參數(shù)是對(duì)象,第二個(gè)參數(shù)是SEL
               typealias IMPCType = @convention(c) (Any, Selector) -> ()
               // 將imp強(qiáng)轉(zhuǎn)為兼容c的函數(shù)指針
               let originalPrintName = unsafeBitCast(originalImp, to: IMPCType.self)
               // 執(zhí)行原方法 類似super.originFuncion()
               originalPrintName(self, selector)
               print("Dynamic")
               // 你想要做的事
               action?()
           }
           // imp_implementationWithBlock的參數(shù)需要的是一個(gè)oc的block,所以需要指定convention(block)
           let implementation = imp_implementationWithBlock(printName)
           // 將方法加入到類的方法列表中
           class_addMethod(dynamicClass, selector, implementation, "v@:")
       } else if let dynamicClass = NSClassFromString(classFullName) {
           // 如果類已經(jīng)存在則直接轉(zhuǎn)換
           object_setClass(self, dynamicClass)
       }
   }
}

其中:
@convention(swift) : 表明這個(gè)是一個(gè)swift的閉包
@convention(block) :表明這個(gè)是一個(gè)兼容oc的block的閉包
@convention(c) : 表明這個(gè)是兼容c的函數(shù)指針的閉包。

定義一個(gè)People類:

class People: NSObject {
    var name: String
    override init() {
        self.name = ""
        super.init()
    }
    
    init(name: String) {
        self.name = name
    }

    //需要添加dynamic不然是靜態(tài)派發(fā)會(huì)走原方法(或者用perform去調(diào)用)
    @objc dynamic func logName() {
        print(name)
    }
    
    @objc dynamic class func decInfo() {
        print("People")
    }
}
let p = People(name: "Albert")
p.dynamicCreatClass(selector: #selector(People.logName), action: nil)
p.logName()

// 輸出如下
Albert
Dynamic

方案2:直接替換當(dāng)前類中的方法

extension NSObject {
  //替換實(shí)例方法
    class func dynamicChangeInstanceMethod(selector: Selector, action: (() -> Void)?) {
        // 獲取實(shí)例方法的IMP
        let method = class_getInstanceMethod(self, selector)
        if let method = method, self.init().responds(to: selector) {
            // 獲取原來方法的IMP
            let oldImp = method_getImplementation(method)
            // 定義一個(gè)方法類型與msgSend的參數(shù)類似 第一個(gè)參數(shù)是對(duì)象,第二個(gè)參數(shù)是SEL
            typealias IMPCType = @convention(c) (Any, Selector) -> Void
            // 將imp強(qiáng)轉(zhuǎn)為兼容c的函數(shù)指針
            let oldImpBlock = unsafeBitCast(oldImp, to: IMPCType.self)
            // 實(shí)現(xiàn)新的方法
            let newFuncion: @convention(block) (Any?) -> Void = {
                (sself) in
                // 執(zhí)行原來的方法類似調(diào)用super
                oldImpBlock(sself, selector)
                print("dynamicChangeInstanceMethod")
                // 你要做的事
                action?()
            }
            // imp_implementationWithBlock的參數(shù)需要的是一個(gè)oc的block,所以需要指定convention(block)
            let imp = imp_implementationWithBlock(newFuncion)
            // 用新方法替換舊方法
            method_setImplementation(method, imp)
        }
    }
    
    //替換類方法
    class func dynamicChangeClassMethod(selector: Selector, action: (() -> Void)?) {
        // 獲取類方法的IMP
        let method = class_getClassMethod(self, selector)
        if let method = method, self.responds(to: selector) {
            // 獲取原來方法的IMP
            let oldImp = method_getImplementation(method)
            // 定義一個(gè)方法類型與msgSend的參數(shù)類似 第一個(gè)參數(shù)是對(duì)象,第二個(gè)參數(shù)是SEL
            typealias IMPCType = @convention(c) (Any, Selector) -> Void
            // 將imp強(qiáng)轉(zhuǎn)為兼容c的函數(shù)指針
            let oldImpBlock = unsafeBitCast(oldImp, to: IMPCType.self)
            // 實(shí)現(xiàn)新的方法
            let newFuncion: @convention(block) (Any) -> Void = {
                (sself) in
                // 執(zhí)行原來的方法類似調(diào)用super
                oldImpBlock(sself, selector)
                print("dynamicChangeClassMethod")
                // 你要做的事
                action?()
            }
            // imp_implementationWithBlock的參數(shù)需要的是一個(gè)oc的block,所以需要指定convention(block)
            let imp = imp_implementationWithBlock(newFuncion)
            // 用新方法替換舊方法
            method_setImplementation(method, imp)
        }
    }
}
People.dynamicChangeInstanceMethod(selector: #selector(People.logName), action: nil)
People.dynamicChangeClassMethod(selector: #selector(People.decInfo), action: nil)
let p = People(name: "Albert")
p.logName()
People.decInfo()

//輸出如下
Albert
dynamicChangeInstanceMethod
People
dynamicChangeClassMethod

方案1中的方法原方法還在MethodList中只是訪問不到,方案2中是替換了原來的方法實(shí)現(xiàn)(替換了IMP指針)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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