1. 静态派发(Static Dispatch)
静态派发在编译时确定方法的具体实现,调用时直接跳转到该实现。静态派发的优点是性能高,因为不需要运行时查找方法实现。
适用场景:
-
值类型(Struct 和 Enum):值类型的方法默认使用静态派发。
-
Final 类和方法:标记为
final
的类或方法无法被继承或重写,因此使用静态派发。 -
全局函数和静态方法:这些方法在编译时就能确定实现,使用静态派发。
示例:
struct Point {var x: Intvar y: Intfunc description() -> String {return "(\(x), \(y))"}
}let p = Point(x: 10, y: 20)
print(p.description()) // 静态派发
2. 动态派发(Dynamic Dispatch)
动态派发在运行时确定方法的具体实现。Swift 中的动态派发主要通过虚表(VTable)和消息转发(Message Forwarding)实现。
2.1 虚表派发(VTable Dispatch)
虚表派发是类方法默认的派发方式。每个类都有一个虚表,其中存储了该类所有可重写方法的指针。子类继承父类的虚表,并可以覆盖其中的方法指针。
适用场景:
-
类的非 Final 方法:类的方法默认使用虚表派发,除非标记为
final
。 -
继承和重写:子类可以重写父类的方法,运行时根据对象的实际类型调用正确的方法。
示例:
class Animal {func makeSound() {print("Some sound")}
}class Dog: Animal {override func makeSound() {print("Bark")}
}let animal: Animal = Dog()
animal.makeSound() // 动态派发,输出 "Bark"
2.2 消息转发(Message Forwarding)
消息转发是 Objective-C 的派发机制,Swift 通过 @objc
和 dynamic
关键字支持这种派发方式。消息转发允许在运行时动态解析方法调用,甚至可以在运行时修改方法实现。
适用场景:
-
与 Objective-C 交互:标记为
@objc
的方法使用消息转发。 -
动态方法解析:标记为
dynamic
的方法使用消息转发,允许在运行时修改方法实现。
示例:
class MyClass {@objc dynamic func sayHello() {print("Hello")}
}let instance = MyClass()
instance.sayHello() // 消息转发
3. 协议派发(Protocol Witness Table Dispatch)
协议方法使用协议见证表(Protocol Witness Table, PWT)进行派发。每个遵循协议的类型都有一个 PWT,其中存储了协议方法的实现指针。
适用场景:
-
协议方法:协议中的方法默认使用 PWT 派发。
-
泛型约束:泛型类型约束为协议时,使用 PWT 派发。
示例:
protocol Greetable {func greet()
}struct Person: Greetable {func greet() {print("Hello")}
}let greeter: Greetable = Person()
greeter.greet() // 协议派发
4. 特殊场景
4.1 泛型方法派发
泛型方法在编译时生成特定类型的实现,通常使用静态派发。但如果泛型类型约束为协议,则使用协议派发。
示例:
func printGreeting<T: Greetable>(_ greeter: T) {greeter.greet() // 协议派发
}let person = Person()
printGreeting(person) // 输出 "Hello"
4.2 扩展中的方法派发
扩展中的方法默认使用静态派发,即使是对类类型的扩展。如果扩展中的方法被重写,仍然使用静态派发。
示例:
class MyClass {func sayHello() {print("Hello from MyClass")}
}extension MyClass {func sayGoodbye() {print("Goodbye from MyClass")}
}class SubClass: MyClass {override func sayHello() {print("Hello from SubClass")}// 无法重写扩展中的方法
}let instance: MyClass = SubClass()
instance.sayHello() // 动态派发,输出 "Hello from SubClass"
instance.sayGoodbye() // 静态派发,输出 "Goodbye from MyClass"
4.3 @objc
和 dynamic
方法
标记为 @objc
的方法使用消息转发,允许与 Objective-C 交互。标记为 dynamic
的方法也使用消息转发,允许在运行时修改方法实现。
示例:
class MyClass {@objc dynamic func sayHello() {print("Hello")}
}let instance = MyClass()
instance.sayHello() // 消息转发
4.4 final
关键字
标记为 final
的类或方法无法被继承或重写,因此使用静态派发。
示例:
final class MyFinalClass {func sayHello() {print("Hello")}
}let instance = MyFinalClass()
instance.sayHello() // 静态派发
总结
Swift 的方法派发机制灵活且高效,支持多种派发方式以适应不同的场景:
-
静态派发:适用于值类型、Final 类和方法,性能最高。
-
动态派发:适用于类的继承和重写,通过虚表派发。
-
协议派发:适用于协议方法,通过协议见证表派发。
-
消息转发:适用于与 Objective-C 交互和动态方法解析。
理解这些派发机制有助于编写高效且符合预期的 Swift 代码。