微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Swift运行时 – 调用超类方法

我正在运行时创建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));

但是Swift中没有objc_msgSendSuper方法.我应该用什么来做同样的事情?

As Martin says,objc_msgSendSuper在Swift中不可用,因为它是一个C variadic函数,由于缺乏类型安全性,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,使用C结构类型在Obj-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_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)).

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐