Swift运行时-调用超类方法

时间:2019-01-02 06:53:39

标签: swift objective-c-runtime

我正在运行时创建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中不可用。我应该使用什么来执行相同的操作?

1 个答案:

答案 0 :(得分:3)

As Martin saysobjc_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)

// 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 = class_getMethodImplementation_stret(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_getMethodImplementationclass_getMethodImplementation_stret之间的区别是由于调用约定的不同–字长类型可以通过寄存器传回,但是较大尺寸的结构需要间接传回。这对class_getMethodImplementation很重要,因为在对象不响应选择器的情况下,它可能传回一个thunk进行消息转发。

另一种选择是使用method_getImplementation,它不执行消息转发,因此不需要区分stret和非stret。

例如:

let impl = method_getImplementation(class_getInstanceMethod(superclass, selector)!)

但是请记住,the documentation notes

  

class_getMethodImplementation可能比method_getImplementation(class_getInstanceMethod(cls, name))快。