派发效率从高到底:Static dispatch
> Table dispatch
> Message dispatch
1.1 static dispatch
Static dispatch
静态派发,即直接地址调用。这个函数指针在编译、链接完成后就确定了,存放在代码段。
优点:派发速度最快,因为需要调用的指令集少,且编译器还有很大的优化空间(如:函数内敛 inline)。
缺点:局限也是最大的,因为缺乏动态性,所以没法支持继承。
1.2 table dispatch
Table dispatch
函数表派发,是编译型语言实现动态行为最常见的实现方式。函数表使用一个数组来存储类声明的每个函数的指针。大部分语言把这个称之为 Virtual Table
虚函数表,Swift 里称为 Witness Table
。
每个类维护一个虚函数表,记录着类的所有函数。如果被 override 的话,表里只会保存 override 后的函数。子类新增函数会被插到这个数组的最后,没有位置可以让 extension 安全的插入函数。
优点:可扩展
缺点:速度慢,编译器对某些含有副作用的函数无法优化
1.3 objc_msgSend
基于 Objc RunTime 实现,沿着实例的 isa 指针进行查找,找不到最后还有3次拯救机会。详细可见:iOS_Objective-C 消息发送(消息查找 及 消息转发)过程
优点:最动态的方式,可以实现 KVO、UIAppearance 和 CoreData 等功能。可在运行时改变函数行为。不只可以通过 swizzling 来改变,甚至可以用 isa-swizzling 修改对象继承关系,可以在面向对象基础上实现自定义派发
确定:速度最慢
2.派发类型识别
2.1 Struct / Enum
Struct
和 Enum
为值类型,不支持继承,它不需要一个 Table 来记录方法信息。所以它的方法调用(包括协议方法),都是静态派发的。