Swift 运行时 - 调用超类方法
Swift runtime - calling superclass method
我正在运行时创建 UIView 的子类并为其提供 layoutSubviews
方法的实现。我需要做的一件重要事情是执行 super.layoutSubviews()
。在 Objective-C 中,我可以使用 objc_msgSendSuper
函数来完成:
Class objectClass = object_getClass(object);
Class superclass = class_getSuperclass(objectClass);
struct objc_super superInfo;
superInfo.receiver = object;
superInfo.super_class = superclass;
typedef void *(*ObjCMsgSendSuperReturnVoid)(struct objc_super *, SEL);
ObjCMsgSendSuperReturnVoid sendMsgReturnVoid = (ObjCMsgSendSuperReturnVoid)objc_msgSendSuper;
sendMsgReturnVoid(&superInfo, @selector(layoutSubviews));
但是 objc_msgSendSuper
方法在 Swift 中不可用。我应该用什么来执行同样的操作?
、objc_msgSendSuper
在 Swift 中不可用,因为它是一个 C 可变参数函数,Swift 由于缺乏类型安全性而不会导入.
一种替代方法是使用 class_getMethodImplementation
以获得指向函数的指针,以调用给定 class 类型的选择器。从那里,您可以将它转换为 Swift 可以使用 unsafeBitCast
调用的函数类型,注意参数和 return 类型匹配。
例如:
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)
// The function to call for a message send of "foo" to a `C` object.
let impl = class_getMethodImplementation(superclass, selector)!
// @convention(c) tells Swift this is a bare function pointer (with no context object)
// All Obj-C method functions have the receiver and message as their first two parameters
// Therefore this denotes a method of type `() -> Void`, which matches up with `foo`
typealias ObjCVoidVoidFn = @convention(c) (AnyObject, Selector) -> Void
let fn = unsafeBitCast(impl, to: ObjCVoidVoidFn.self)
fn(d, selector) // C's foo
请注意,与 objc_msgSendSuper
一样,它假定桥接到 Obj-C 的 return 类型与指针布局兼容。在大多数情况下(包括你的)这是正确的,但对于 returning 类型的方法来说并非如此 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) // (2.0, 3.0, 4.0, 5.0)
class_getMethodImplementation
和class_getMethodImplementation_stret
之间的区别是调用约定的不同——字大小的类型可以通过寄存器传回,但是更大的结构需要传递间接回来。这对 class_getMethodImplementation
很重要,因为它可以在对象不响应选择器的情况下传回用于消息转发的 thunk。
另一种选择是使用method_getImplementation
,它不执行消息转发,因此不需要区分stret 和非stret。
例如:
let impl = method_getImplementation(class_getInstanceMethod(superclass, selector)!)
但是请记住 the documentation notes:
class_getMethodImplementation
may be faster than method_getImplementation(class_getInstanceMethod(cls, name))
.
我正在运行时创建 UIView 的子类并为其提供 layoutSubviews
方法的实现。我需要做的一件重要事情是执行 super.layoutSubviews()
。在 Objective-C 中,我可以使用 objc_msgSendSuper
函数来完成:
Class objectClass = object_getClass(object);
Class superclass = class_getSuperclass(objectClass);
struct objc_super superInfo;
superInfo.receiver = object;
superInfo.super_class = superclass;
typedef void *(*ObjCMsgSendSuperReturnVoid)(struct objc_super *, SEL);
ObjCMsgSendSuperReturnVoid sendMsgReturnVoid = (ObjCMsgSendSuperReturnVoid)objc_msgSendSuper;
sendMsgReturnVoid(&superInfo, @selector(layoutSubviews));
但是 objc_msgSendSuper
方法在 Swift 中不可用。我应该用什么来执行同样的操作?
objc_msgSendSuper
在 Swift 中不可用,因为它是一个 C 可变参数函数,Swift 由于缺乏类型安全性而不会导入.
一种替代方法是使用 class_getMethodImplementation
以获得指向函数的指针,以调用给定 class 类型的选择器。从那里,您可以将它转换为 Swift 可以使用 unsafeBitCast
调用的函数类型,注意参数和 return 类型匹配。
例如:
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)
// The function to call for a message send of "foo" to a `C` object.
let impl = class_getMethodImplementation(superclass, selector)!
// @convention(c) tells Swift this is a bare function pointer (with no context object)
// All Obj-C method functions have the receiver and message as their first two parameters
// Therefore this denotes a method of type `() -> Void`, which matches up with `foo`
typealias ObjCVoidVoidFn = @convention(c) (AnyObject, Selector) -> Void
let fn = unsafeBitCast(impl, to: ObjCVoidVoidFn.self)
fn(d, selector) // C's foo
请注意,与 objc_msgSendSuper
一样,它假定桥接到 Obj-C 的 return 类型与指针布局兼容。在大多数情况下(包括你的)这是正确的,但对于 returning 类型的方法来说并非如此 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) // (2.0, 3.0, 4.0, 5.0)
class_getMethodImplementation
和class_getMethodImplementation_stret
之间的区别是调用约定的不同——字大小的类型可以通过寄存器传回,但是更大的结构需要传递间接回来。这对 class_getMethodImplementation
很重要,因为它可以在对象不响应选择器的情况下传回用于消息转发的 thunk。
另一种选择是使用method_getImplementation
,它不执行消息转发,因此不需要区分stret 和非stret。
例如:
let impl = method_getImplementation(class_getInstanceMethod(superclass, selector)!)
但是请记住 the documentation notes:
class_getMethodImplementation
may be faster thanmethod_getImplementation(class_getInstanceMethod(cls, name))
.