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

 投递快手等公司10个岗位
投递快手等公司10个岗位
 查看7道真题和解析
查看7道真题和解析