函数派发
英文名称 Method Dispatch,
使用哪种途径去调用一个函数的机制,也就是CPU在内存中查找函数地址并调用的过程。
对于编译型语言来讲,主要三种类型的函数派发方式:
Direct Dispatch
也被称作 Static Dispatch
CPU直接使用函数地址调用,使用最少的指令,速度最快。
支持编译器优化, 比如内联(inline),能极大提高程序运行效率。
包含如下情形
- C: 全部的函数
- C++: 默认方式
- Swift
- 结构体方法
- 枚举方法
- 加了final关键字的class方法
- extension方法
- Objective-C
- load方法,因为是C函数调用,所以是直接派发
Table Dispatch
类维护一个虚函数表(V-Table),Swift 里称为witness-table。该函数表存取了每个函数实现的地址。
效率很快,但是没有直接派发快,因为多了查找虚函数表和方法地址这两项工作
编译器无法进行 inline 等优化工作。
包含如下情形
- C++加了virtual的方法
- Swift class
消息派发
走Runtime objc_msgSend流程。效率不高,需要通过缓存来提高效率.这样查找性能就和函数表派发差不多了.
- Objective-C NSObject方法
- Swift中只有加了@objc dynamic关键字的方法
汇编区别
- 直接派发: callq+内存地址
- table dispatch: callq+寄存器偏移
- 消息派发: callq+objc_msgSend
inline 优化
针对static dispatch, 编译器会自动设置内联来提高效率,我们也可以使用 @inline 指令来自己设置内联方式
- @inline(never) 声明这个函数不被编译成 inline 的形式,即使开启了编译器优化;
- @inline(__always) 声明这个函数总是编译成 inline 的形式
- 部分关键函数进行内联优化,可以增大
逆向难度。
原则
理解了 Swift 中的方法派发方式后,我们应该遵循如下原则
- 应该优先使用静态派发,来获得最佳的性能,(结构体、枚举+class final function)
- 需要继承关系, 使用class function
- 只有在需要和 Objective-C 代码交互时才应该使用消息派发,因为它最慢。