Swift开发

使用Runtime替换实例方法和类方法实现

2020-05-04  本文已影响0人  林夕copy

方案1.通过动态创建类和实现其中的方法,然后把原对象的类型修改成你创建的对象通过消息派发使对象调用你新实现的方法

extension NSObject {
 //动态创建类并重写方法
   func dynamicCreatClass(selector: Selector, action: (() -> ())?) {
       // 创建类的类名
       let classFullName = "ATDynamic_\(self.classForCoder.description()))"
       // 获取原来类
       let originalClass = type(of: self)
       
       // 判断这个类是否已经存在
       if let dynamicClass = objc_allocateClassPair(originalClass, classFullName, 0) {
           // 动态的创建这个类
           objc_registerClassPair(dynamicClass)
           // 将原对象修改成新的类型
           object_setClass(self, dynamicClass)
           
           // 实现新的方法
           let printName: @convention(block) (Any?) -> () = { nullSelf in
               guard let _ = nullSelf else { return }
               // 获取原来类中方法的Imp
               let originalImp = class_getMethodImplementation(originalClass, selector)
               // 定义一个方法类型与msgSend的参数类似 第一个参数是对象,第二个参数是SEL
               typealias IMPCType = @convention(c) (Any, Selector) -> ()
               // 将imp强转为兼容c的函数指针
               let originalPrintName = unsafeBitCast(originalImp, to: IMPCType.self)
               // 执行原方法 类似super.originFuncion()
               originalPrintName(self, selector)
               print("Dynamic")
               // 你想要做的事
               action?()
           }
           // imp_implementationWithBlock的参数需要的是一个oc的block,所以需要指定convention(block)
           let implementation = imp_implementationWithBlock(printName)
           // 将方法加入到类的方法列表中
           class_addMethod(dynamicClass, selector, implementation, "v@:")
       } else if let dynamicClass = NSClassFromString(classFullName) {
           // 如果类已经存在则直接转换
           object_setClass(self, dynamicClass)
       }
   }
}

其中:
@convention(swift) : 表明这个是一个swift的闭包
@convention(block) :表明这个是一个兼容oc的block的闭包
@convention(c) : 表明这个是兼容c的函数指针的闭包。

定义一个People类:

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

    //需要添加dynamic不然是静态派发会走原方法(或者用perform去调用)
    @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:直接替换当前类中的方法

extension NSObject {
  //替换实例方法
    class func dynamicChangeInstanceMethod(selector: Selector, action: (() -> Void)?) {
        // 获取实例方法的IMP
        let method = class_getInstanceMethod(self, selector)
        if let method = method, self.init().responds(to: selector) {
            // 获取原来方法的IMP
            let oldImp = method_getImplementation(method)
            // 定义一个方法类型与msgSend的参数类似 第一个参数是对象,第二个参数是SEL
            typealias IMPCType = @convention(c) (Any, Selector) -> Void
            // 将imp强转为兼容c的函数指针
            let oldImpBlock = unsafeBitCast(oldImp, to: IMPCType.self)
            // 实现新的方法
            let newFuncion: @convention(block) (Any?) -> Void = {
                (sself) in
                // 执行原来的方法类似调用super
                oldImpBlock(sself, selector)
                print("dynamicChangeInstanceMethod")
                // 你要做的事
                action?()
            }
            // imp_implementationWithBlock的参数需要的是一个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)
            // 定义一个方法类型与msgSend的参数类似 第一个参数是对象,第二个参数是SEL
            typealias IMPCType = @convention(c) (Any, Selector) -> Void
            // 将imp强转为兼容c的函数指针
            let oldImpBlock = unsafeBitCast(oldImp, to: IMPCType.self)
            // 实现新的方法
            let newFuncion: @convention(block) (Any) -> Void = {
                (sself) in
                // 执行原来的方法类似调用super
                oldImpBlock(sself, selector)
                print("dynamicChangeClassMethod")
                // 你要做的事
                action?()
            }
            // imp_implementationWithBlock的参数需要的是一个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中是替换了原来的方法实现(替换了IMP指针)

上一篇 下一篇

猜你喜欢

热点阅读