按照类型划分
Value Type
像struct、enum这样的值类型,不支持继承,所以无需动态派发,它所有的方法调用,包括遵循的协议方法,都是静态派发;
Swift Class Type
对于一个纯 Swift class 来说,默认使用 Virtual Table 派发,在运行时查找到这个方法并进行调用;。
被标记成 final、private/fileprivete和internal(开启WMO)的类或方法 ,编译器就会知道这个方法不会被继承或者 override,会优化成成静态派发。
在extension中的方法,默认是静态派发;
当方法被标记为 @objc dymanic,此时这个方法会使用 Message派发。
NSObject Subclass
继承并重载OC基类的方法,是Message派发。
标记为 final、private/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类型划分消息派发方式
| 对象/派发方式 | 静态派发 | VTable | WTable | 消息派发 |
|---|---|---|---|---|
| Value Type | 默认行为 | 无 | :protocol | 无 |
| Swift Class | final、extension、private/fileprivete 和internal(WMO) | 默认行为 | :protocol | @objc dynamic |
| NSObject | final、extension、private/fileprivete 和internal(WMO) | 默认行为 | :protocol | @objc |
| protocol | extension | 默认行为 | 无 | :NSObjectProtocol @objc |
注意:Witness Table 仅在调用对象类型为 Protocol 类型时,才会被引用。
派发方式总结
静态派发(直接调用)
- 值类型,如
struct、enum,因为不支持继承,所有无动态性可言; - 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 指令进行法的调用。