Swift的方法派发方式

2,184 阅读2分钟

按照类型划分

Value Type

像struct、enum这样的值类型,不支持继承,所以无需动态派发,它所有的方法调用,包括遵循的协议方法,都是静态派发

Swift Class Type

对于一个纯 Swift class 来说,默认使用 Virtual Table 派发,在运行时查找到这个方法并进行调用;。

被标记成 finalprivate/filepriveteinternal(开启WMO)的类或方法 ,编译器就会知道这个方法不会被继承或者 override,会优化成成静态派发

extension中的方法,默认是静态派发

当方法被标记为 @objc dymanic,此时这个方法会使用 Message派发

NSObject Subclass

继承并重载OC基类的方法,是Message派发

标记为 finalprivate/fileprivete 和internal(WMO)的函数可以参考上面的 Swift class。

非extension中定义的普通方法和标记为 @objc 的方法都使用 V-Table派发

@objc 只是把方法暴露给 Objective-C,并没有改变方法派发的本质。

Extension 中的方法是静态派发的,但标记为 @objc 的函数需要对 Objc runtime 可见,就变成了 Message 派发。而且加不加 dynamic 生成的底层代码是一样的,这里怀疑是编译器隐式的加上了 dynamic 关键字。

Protocol type

默认使用 Witness Table 进行派发;

注意,必须转换为protocol type,再去调用的方法才会使用Witness Table 派发.

根据Type类型划分消息派发方式

对象/派发方式静态派发VTableWTable消息派发
Value Type默认行为:protocol
Swift Classfinal、extension、private/fileprivete 和internal(WMO)默认行为:protocol@objc dynamic
NSObjectfinal、extension、private/fileprivete 和internal(WMO)默认行为:protocol@objc
protocolextension默认行为:NSObjectProtocol @objc

注意:Witness Table 仅在调用对象类型为 Protocol 类型时,才会被引用。

派发方式总结

静态派发(直接调用)

  • 值类型,如structenum,因为不支持继承,所有无动态性可言;
  • extension中的方法,默认是静态派发,如果使用@objc dynamic修饰,会改变为消息派发
  • final/private/fileprivate修饰的类和方法,由于不支持继承或者重写,是静态派发;
  • internal: 在开启Whole-Moddule Optimization(WMO)时,原理同private,也是静态派发;

在SIL(Swift Intermediate Language)中function_ref  指令用于生成值类型函数的引用。

Message派发

  • Swift中继承并重新了OC基类的方法;
  • Swift Class中,使用@objc dynamic修饰的方法;
  • NSObject Subclass中,使用@objc dynamic修饰的方法,或者在extension中使用@objc修饰的方法;

在SIL中使用 objc_method 指令进行法的调用。

函数表派发

除了上述情况,其他都是函数表派发。

在SIL中使用 class_method 指令进行法的调用。