派发(dispatch)是一个比较通用的概念,一般是指为了完成某个目的把一个东西发送到某个位置的行为。在计算机科学中,这个术语在很多地方都会用到,比如派发一个调用给某个函数,派发一个事件给一个监听者,派发一个中断给中断处理程序,或者派发一个进程给 CPU。
在这篇文章中,我们主要研究 Swift 中的派发,也就是派发一个调用到某个方法上,Swift 中的方法派发包括类的方法派发和基于协议的派发。
类的方法派发
Swift 中类的方法的派发有以下三种方式:
- 静态派发(Static Dispatch)
- 动态派发(Dynamic Dispatch)
- 消息派发(Messaging Dispatch)
静态派发
静态派发,又叫做早期绑定,是指在编译期将方法调用绑定到方法的实现上,这种派发方式非常快。在编译期,编译器可以看到调用方和被调方的所有信息,直接生成跳转代码,这样在运行期就不会有其它额外的开销。并且编译器可以根据自己知道的信息进行优化,比如内联,可以极大提高程序运行效率。
在 Swift 中,结构体和枚举的方法调用,以及被 final
标记的类和类的方法,都会采用这种派发方式。
动态派发
动态派发是在运行时决定方法调用地址,因此需要有个查找方法地址的机制,在 Swift 中是通过虚函数表(Virtual Method Table),简称 V-Table 实现的,因此动态派发也被称为表派发(Table Dispatch)
在编译期,编译器会给每个包含动态派发方法的类型创建一个虚函数表,这个表会被放在内存的静态区,表中是方法名到方法实现地址的映射。当这个类型的方法被调用时,运行时会去这个类型的虚函数表中寻找这个方法名对应的实现地址,然后再跳转到这个地址执行代码。
动态派发主要是用来实现继承多态,继承多态是多态的一种。例如以下代码:
class Animal {
func makeNoise() {
fatalError("此方法必须通过子类调用")
}
}
class Dog: Animal {
override func make