Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/file/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift运行时调用超类方法_Swift_Objective C Runtime - Fatal编程技术网

Swift运行时调用超类方法

Swift运行时调用超类方法,swift,objective-c-runtime,Swift,Objective C Runtime,我正在运行时创建UIView的一个子类,并为其提供了layoutSubviews方法的实现。我需要做的一件重要事情是执行super.layoutSubviews()。在Objective-C中,我可以使用objc\u msgSendSuper功能来实现: Class objectClass = object_getClass(object); Class superclass = class_getSuperclass(objectClass); struct objc_super super

我正在运行时创建UIView的一个子类,并为其提供了
layoutSubviews
方法的实现。我需要做的一件重要事情是执行
super.layoutSubviews()
。在Objective-C中,我可以使用
objc\u 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\u msgSendSuper
方法。我应该使用什么来执行相同的操作?

objc\u msgSendSuper
在Swift中不可用,因为它是一个C变量函数,Swift由于缺乏类型安全性而无法导入

另一种方法是使用以获取指向函数的指针,以调用给定类类型上的选择器。从那里,您可以将其转换为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
let impl = method_getImplementation(class_getInstanceMethod(superclass, selector)!)
注意,与objc_msgSendSuper类似,这假定桥接到Obj-C的返回类型与指针布局兼容。这在大多数情况下(包括您的情况)都是正确的,但对于返回类型(如
CGRect
)的方法则不正确,该类型在Obj-C中使用C结构类型表示

对于这些情况,您需要使用
class\u getMethodImplementation\u 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\u getMethodImplementation
可能比
method\u getImplementation(class\u getInstanceMethod(cls,name))
更快


正如另外一句话:objc_msgSend(Super)在Swift中不可用,因为它需要一个变量参数列表。这个问题可能有解决办法,但您的方法看起来更简单、更安全:您不必考虑objc_msgSendSuper与objc_msgSendSuper_stret与objc_msgSendSuper_fpret…@MartinR Yup–尽管如此,如果被调用的方法返回了一个结构,则必须使用
class\u getMethodImplementation\u stret
(这似乎是因为在对象不响应选择器的情况下,它可能会返回消息转发的thunk)。不过,如果改用
class\u getMethodImplementation
,这种区别看起来会自动产生。关于这一点,我将在我的回答中添加一个注释。我没有注意到类_getMethodImplementation也有这些变体…@MartinR奇怪的是,没有
类_getMethodImplementation_fpret
”\_(ツ)_/'(与此问题无关:您是否查看了)