正如Martin所说的那样, objc_msgSendSuper
在Swift中不可用,因为它是一个C变参函数,而Swift由于缺乏类型安全性而无法导入。
其中一种替代方法是使用class_getMethodImplementation
来获取在给定类类型上调用选择器的函数指针。从那里,您可以将其强制转换为Swift可以使用unsafeBitCast
调用的函数类型,确保参数和返回类型匹配。
例如:
import Foundation
class C {
@objc func foo() {
print("C's foo")
}
}
class D : C {
override func foo() {
print("D's foo")
}
}
let d = D()
let superclass: AnyClass = class_getSuperclass(type(of: d))!
let selector = #selector(C.foo)
let impl = class_getMethodImplementation(superclass, selector)!
typealias ObjCVoidVoidFn = @convention(c) (AnyObject, Selector) -> Void
let fn = unsafeBitCast(impl, to: ObjCVoidVoidFn.self)
fn(d, selector)
请注意,与
objc_msgSendSuper
类似,这假定返回类型在Obj-C中的桥接与指针具有相同的布局兼容性。 在大多数情况下(包括您的情况),这是正确的,但对于返回
CGRect
等类型的方法,这不是正确的,因为它在Obj-C中使用C结构类型表示。
对于这些情况,您需要改用
class_getMethodImplementation_stret
:
import Foundation
class C {
@objc func bar() -> CGRect {
return CGRect(x: 2, y: 3, width: 4, height: 5)
}
}
class D : C {
override func bar() -> CGRect {
return .zero
}
}
let d = D()
let superclass: AnyClass = class_getSuperclass(type(of: d))!
let selector = #selector(C.bar)
let impl = <b>class_getMethodImplementation_stret</b>(superclass, selector)!
typealias ObjCVoidVoidFn = @convention(c) (AnyObject, Selector) -> CGRect
let fn = unsafeBitCast(impl, to: ObjCVoidVoidFn.self)
let rect = fn(d, selector)
print(rect)
class_getMethodImplementation
和
class_getMethodImplementation_stret
之间的区别在于调用约定的不同 - 一个字大小的类型可以通过寄存器传回,但是大于此大小的结构需要通过间接方式传回。这对于
class_getMethodImplementation
很重要,因为在对象不响应选择器的情况下,它可能会传回消息转发的thunk。
另一个选择是使用
method_getImplementation
,它不执行消息转发,因此不需要区分stret和非stret。
例如:
let impl = method_getImplementation(class_getInstanceMethod(superclass, selector)!)
然而请记住
文档注释:
class_getMethodImplementation
可能比 method_getImplementation(class_getInstanceMethod(cls, name))
更快。
class_getMethodImplementation_stret
(这似乎是因为在对象不响应选择器的情况下,它可能会返回一个消息转发的 thunk)。虽然如果您使用class_getMethodImplementation
,则似乎会自动进行此区分。我将在我的答案中添加一条注释以说明这一点。 - Hamish