美文网首页Swift开发
使用Runtime替换实例方法和类方法实现

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

作者: 林夕copy | 来源:发表于2020-05-04 19:54 被阅读0次

方案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指针)

相关文章

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

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

  • class_getInstanceMethod和class_ge

    在开发中有的时候需要获得系统的类,实例方法与类方法,通过自己重写此方法进行IMP指针替换,实现自定义的方法实现替换...

  • Runtime

    runtime运行时机制1:通过runtime,实现方法交换(交换两个类方法、交换两个实例方法)2:通过runti...

  • Day3

    1 runtime运行时机制1:通过runtime,实现方法交换(交换两个类方法、交换两个实例方法)。2:通过ru...

  • objective-c runtime method

    重新认识+和-方法 +:(Class)类方法。-:(Instance)实例方法。 实质上对于runtime而言,并...

  • runtime-Method Swizzling(方法调换)

    通过修改一个已存在类的方法, 来实现方法替换是比较常用的runtime技巧。 使所有的类都具有调换方法的功能: #...

  • iOS字体大小自动适配

    扩展UIfont,然后使用Runtime替换方法

  • iOS-监听键盘删除按钮的点击事件

    核心思想:利用Runtime动态替换方法,拦截deleteBackward方法实现自定义代理具体看下面代码 在使用...

  • runtime 在逆向中的使用

    在逆向工程中,利用runtime可以动态获取类和属性,绑定属性,替换方法的实现。 KVC 可以通过直接获取类的私有...

  • Category实现原理

    依赖runtime 动态的将分类的方法和类方法合并到类对象和元类对象的方法列表中 (对实例对象 类对象 元类对...

网友评论

    本文标题:使用Runtime替换实例方法和类方法实现

    本文链接:https://www.haomeiwen.com/subject/ndgyghtx.html