Как говорит Мартин , objc_msgSendSuper
недоступно в Swift, поскольку это функция с переменным числом Си, которую Swift не импортирует из-за отсутствия безопасности типов.
Oneальтернативой является использование 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)
// 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, совместим с макетом с указателем.Это верно в большинстве случаев (включая ваш), но не будет верно для метода, возвращающего тип, такой как 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
, который не выполняетпересылка сообщений и, следовательно, не нужно различать растяжение и не растяжение.
Например:
let impl = method_getImplementation(class_getInstanceMethod(superclass, selector)!)
Однако имейте в виду, что в документации отмечает :
class_getMethodImplementation
может быть быстрее, чем method_getImplementation(class_getInstanceMethod(cls, name))
.