按照类型划分
Value Type
像struct、enum这样的值类型,不支持继承,所以无需动态派发,它所有的方法调用,包括遵循的协议方法,都是直接调用;
值类型的函数、引用类型的函数且函数修饰词为final、extention中实现的方法(无法被重写)都属于直接派发;
在SIL(Swift Intermediate Language)中unction_ref
指令用于生成值类型函数的引用。找到 speak 方法调用的部分,此处通过 function_ref
直接获取了 Dog.speak
方法的引用,随之调用。
Class Type
对于一个纯 Swift class 来说,默认使用 Table 派发,影响它方法调用的关键字有 final
、 dynamic
和 extension
。
函数如果被标记成 final
,编译器就会知道这个方法不会被 override,并把它的调用方式标记成直接调用。而对于未标记成 final
并在 class 内部(非 extension)中定义的方法,Swift 会用一种叫作 Virtual Table 的机制来在运行时查找到这个方法并进行调用。
当一个方法被标记为 dymanic
,你必须同时把它标记上 @objc
,此时这个方法会使用 Message 调用,依赖 Objc runtime。
NSObject Subclass
影响这种类型的函数调用方式的关键字和上面一样,但是表现却不完全一样。
标记为 final
和 dynamic
的函数可以参考上面的 class。
在原生声明(非 extension)中定义的普通方法和标记为 @objc 的方法都使用 V-Table 机制派发。用 Swift 编写的类是不能被 Objective-C 继承的,@objc 只是把方法暴露给 Objective-C,并没有改变方法派发的本质。
Extension 中的方法是直接派发的,但标记为 @objc 的函数需要对 Objc runtime 可见,就变成了 Message 派发。而且加不加 dynamic
生成的底层代码是一样的,这里怀疑是编译器隐式的加上了 dynamic
关键字。
函数表派发
引用类型中,未经 final/dynamic修饰,且并不是在extension中实现的方法、protocol中的方法,都是使用函数表派发
SIL 使用 class_method 指令去获取应用类型中 VTable 中的方法进行调用,
使用 witness_method 指令获取protocol类型在 WTable 中的方法进行调用
动态(消息)派发
必须是NSObject子类中,添加 @objc dynamic
修饰的方法、或者子类中extension里的@objc
方法,使用动态派发;
只添加@objc,并不会修改动态派发,只是生成了oc和swift可见版本的方法
在SIL中使用 objc_method
指令进行法的调用。
根据Type类型划分消息派发方式
对象/派发方式 | 静态派发 | VTable | WTable | 消息(动态)派发 |
---|---|---|---|---|
Value Type | 默认行为 | 无 | :protocol | 无 |
Swift Class | final、extension | 默认行为 | :protocol | dynamic |
NSObject | final、extension | 默认行为 | :protocol | dynamic |
protocol | extension | 默认行为 | 无 | :NSObjectProtocol @objc |
注意:Witness Table 仅在调用对象类型为 Protocol 类型时,才会被引用。
根据声明所在位置划分
原始位置 | extension | |
---|---|---|
Value Type | static | static |
Class Type | V-Table | static |
NSObject Subclass | V-Table | @objc为Message,其他为Static |
protocol | N/A | static |
派发方式总结
Struct
的方法默认是Static派发
;Extension
内的方法默认是Static派发
;Swift Class
和NSObject子类
内部的方法,默认是V-Table派发
;- 实例类型是
Protocol
的类,调用protocol方法
,是W-Table派发
(注意实例类型必须转换为Protocol); Protocol的默认实现
即Protocol在Extension中的方法,默认是Static派发
;final
修饰的方法,是Static派发
;@objc dynamic
修饰方法,会变为Message派发
;NSObject子类
的Extension方法,使用@objc
修饰,会变为V-Table派发
;