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 可以获取类方法"