iOS和Swift函数派发机制

340 阅读2分钟

函数派发

英文名称 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关键字的方法

汇编区别

image.png image.png

  • 直接派发: callq+内存地址
  • table dispatch: callq+寄存器偏移
  • 消息派发: callq+objc_msgSend

inline 优化

针对static dispatch, 编译器会自动设置内联来提高效率,我们也可以使用 @inline 指令来自己设置内联方式

  • @inline(never) 声明这个函数不被编译成 inline 的形式,即使开启了编译器优化;
  • @inline(__always) 声明这个函数总是编译成 inline 的形式
  • 部分关键函数进行内联优化,可以增大逆向难度

原则

理解了 Swift 中的方法派发方式后,我们应该遵循如下原则

  1. 应该优先使用静态派发,来获得最佳的性能,(结构体、枚举+class final function)
  2. 需要继承关系, 使用class function
  3. 只有在需要和 Objective-C 代码交互时才应该使用消息派发,因为它最慢。