iOS runtime通过selector获取IMP地址

iOS runtime通过selector获取IMP地址

获取IMP地址有两种方法:

  • class_getMethodImplementation
  • (class_getInstanceMethod | class_getClassMethod) --> method_getImplementation

下面请看(代码用swift在playground上完成)

//在swift中获取类名,需要类所在文件名.类名这样拼接。playground中bundleVersion就当做文件名
class Playground : NSObject {
    static var bundleVersion: String {
        let s = self.description().replacingOccurrences(of: ".Playground", with: "")
        return s
    }
}

class Person: NSObject {
    override init() {
        super.init()
        debugPrint("==============class_getMethodImplementation====================")
        getIMPFrom(sel: #selector(instanceFunc))
        getIMPFrom(sel: #selector(Person.classFunc))
        debugPrint("==============method_getImplementation====================")
        getIMPOfMethodFrom(sel: #selector(instanceFunc))
        getIMPOfMethodFrom(sel: #selector(Person.classFunc))
        debugPrint("==============================================================")
        getInstanceIMPFrom(sel: #selector(instanceFunc))
        getClassIMPFrom(sel: #selector(Person.classFunc))
    }
    
    @objc func instanceFunc() {
        debugPrint("instance func")
    }
    
    @objc class func classFunc() {
        debugPrint("class func")
    }
}
//在playground中创建一个Person对象
let p = Person()

一、使用class_getMethodImplementation获取IMP

/// 第一种方式获取IMP: class_getMethodImplementation
    func getIMPFrom(sel: Selector) {
        let clsName = "\(Playground.bundleVersion).Person"
        let instanceCls = objc_getClass(clsName)
        let instanceImp = class_getMethodImplementation(instanceCls as? AnyClass, sel)
        
        let classCls = objc_getMetaClass(clsName)
        let classImp = class_getMethodImplementation(classCls as? AnyClass, sel)
        
        debugPrint(instanceImp!, classImp!)
    }
"==============class_getMethodImplementation===================="
0x000000011defad20 0x0000000105f4da00
0x0000000105f4da00 0x000000011defaeb0

使用class_getMethodImplementation分别获取实例方法、类方法的IMP。打印出来有两个相同的地址0x0000000105f4da00,这是在调用class_getMethodImplementation时无法找到对应的实现方法。(你可以执行多次都会发现这两个地址虽然会变但都会相同)

二、(class_getInstanceMethod | class_getClassMethod) --> method_getImplementation

使用这种方式获取IMP,先通过class_getInstanceMethod获取实例方法method或者通过class_getClassMethod获取类方法method,再调用method_getImplementation方法获取IMP。通过打印分析:传入selector为实例方法时,会找不到对应的类方法实现。同理传入selector为类方法时,也会找不到对应的实例方法实现。

/// 第二种方式获取IMP:先获取method在获取IMP
    func getIMPOfMethodFrom(sel: Selector) {
        let clsName = "\(Playground.bundleVersion).Person"
        let cls = objc_getClass(clsName)
        if let instanceMethod = class_getInstanceMethod(cls as? AnyClass, sel) {
            let instanceImp = method_getImplementation(instanceMethod)
            debugPrint("instanceIMP: \(instanceImp)")
        } else {
            debugPrint("instanceIMP nil")
        }
        
        let mataCls = objc_getMetaClass(clsName)
        if let classMethod = class_getClassMethod(mataCls as? AnyClass, sel) {
            let classImp = method_getImplementation(classMethod)
            debugPrint("classIMP: \(classImp)")
        } else {
            debugPrint("classIMP nil")
        }
    }
"==============method_getImplementation===================="
"instanceIMP: 0x0000000125bc1d20"
"classIMP nil"
"instanceIMP nil"
"classIMP: 0x0000000125bc1eb0"

三、Tips

通过objc_getMetaClass获取的class是无法找到实例方法的实现的。然而使用objc_getClass获取的class能找到类方法的实现。

/// 使用getMetaClass获取实例方法IMP
    func getInstanceIMPFrom(sel: Selector) {
        let clsName = "\(Playground.bundleVersion).Person"
        let cls = objc_getMetaClass(clsName)
        if let method = class_getInstanceMethod(cls as? AnyClass, sel) {
            let instanceIMP = method_getImplementation(method)
            debugPrint("instanceIMP: \(instanceIMP)")
        } else {
            debugPrint("getMetaClass 无法获取实例方法")
        }
    }
    
    /// 使用getClass获取类方法
    func getClassIMPFrom(sel: Selector) {
        let clsName = "\(Playground.bundleVersion).Person"
        let cls = objc_getClass(clsName)
        if let method = class_getClassMethod(cls as? AnyClass, sel) {
            let classIMP = method_getImplementation(method)
            debugPrint("classIMP: \(classIMP)-------getClass 可以获取类方法")
        } else {
            debugPrint("getClass 无法获取类方法")
        }
    }
"=============================================================="
"getMetaClass 无法获取实例方法"
"classIMP: 0x0000000125bc1eb0-------getClass 可以获取类方法"

你可能感兴趣的:(iOS runtime通过selector获取IMP地址)