加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Swift运行时 – 调用超类方法

发布时间:2020-12-14 02:24:38 所属栏目:百科 来源:网络整理
导读:我正在运行时创建UIView的子类,并为它提供layoutSubviews方法的实现.我需要做的一件非常重要的事情就是执行super.layoutSubviews().在Objective-C中我可以使用objc_msgSendSuper函数来完成它: Class objectClass = object_getClass(object);Class superclas
我正在运行时创建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)).

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读