5-17.【性能分析与优化】final class、private、internal 对方法派发有什么影响?

3 阅读2分钟

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️⃣ 关键原则总结

  1. final 优先:类或方法 final → 编译器可静态派发 → 高性能

  2. 访问控制可帮助静态派发

    • private → 编译器知道调用点 → 静态派发
    • internal → 模块内可静态派发
    • public/open → 模块外调用需要动态派发
  3. @inlinable + final/internal/private

    • 提高跨模块特化和静态派发可能性