1️⃣ final class 对派发的影响
特点
final class表示 类不能被继承- 所有方法也就无法被子类重写
- 编译器可以静态派发(直接调用函数指针)
final class C {
func foo() {}
}
let c = C()
c.foo() // 静态派发
-
即使方法不是
final,只要类是final→ 方法默认静态派发 -
优势:
- 去掉 vtable 查找
- 可内联、LLVM 优化
2️⃣ private / internal 对派发的影响
private 方法
-
方法仅在 当前文件内可见
-
编译器能知道 所有调用点
-
可以:
- 静态派发
- 内联优化
- 泛型特化
class C {
private func foo() {}
func call() { foo() } // 静态派发
}
internal 方法
- 方法在 模块内可见
- 模块内调用 → 编译器知道调用目标 → 可静态派发 / 内联
- 模块外调用 → 不可见 → 必须动态派发(除非用 @inlinable)
class C {
internal func bar() {}
}
- 调用方在同模块 → 静态派发
- 调用方在其他模块 → 只能通过动态派发
3️⃣ public / open 对派发的影响
-
public:
- 模块外可调用,但不可继承
- 方法默认动态派发(除非 final 或 @inlinable)
-
open:
- 模块外可调用,可继承 → 方法可被重写
- 调用方必须动态派发(vtable)
4️⃣ 综合派发规则
| 类/方法特性 | 派发方式 | 注释 |
|---|---|---|
| final class 方法 | 静态派发 | 类不可继承 → 方法不可重写 |
| final 方法(非 final class) | 静态派发 | 方法不可重写 → 可静态调用 |
| private 方法 | 静态派发 | 编译器知道调用目标,跨文件不可见 |
| internal 方法 | 同模块调用 → 静态派发 跨模块 → 动态派发 | 编译器可静态优化模块内调用 |
| public 方法 | 跨模块 → 动态派发 | 除非 final / @inlinable |
| open 方法 | 动态派发 | 模块内外均可继承重写 |
5️⃣ 关键原则总结
-
final 优先:类或方法 final → 编译器可静态派发 → 高性能
-
访问控制可帮助静态派发:
- private → 编译器知道调用点 → 静态派发
- internal → 模块内可静态派发
- public/open → 模块外调用需要动态派发
-
@inlinable + final/internal/private:
- 提高跨模块特化和静态派发可能性