4-6.【协议导向编程】protocol extension 的方法调用是 静态派发,这意味着什么?

2 阅读2分钟

一、先给一句“人话版”结论

协议扩展中的方法,是否参与多态,取决于:
它有没有在协议本身中声明。

  • 只写在 protocol extension 里的方法
    👉 静态派发(compile-time)
  • 在 protocol 中声明、extension 里实现的方法
    👉 动态派发(通过 witness table)

这就是 Swift 的“协议扩展静态派发”真正含义。


二、什么是「静态派发」?意味着什么?

静态派发 = 编译期决定调用哪个实现

  • 不看运行时真实类型
  • 不支持 override
  • 不走 witness table
  • 可以内联,性能好
  • ❌ 不多态

📌 本质:像普通函数一样调用


三、经典踩坑示例(一定要看)

1️⃣ 只在 extension 里定义方法(❌ 多态)

protocol Greeter {}

extension Greeter {
    func greet() {
        print("Hello from protocol extension")
    }
}

struct Person: Greeter {
    func greet() {
        print("Hello from Person")
    }
}

你可能以为:Person 覆盖了 greet
但 Swift 并不这么看


调用对比(重点)

let p = Person()
p.greet()

✅ 输出:

Hello from Person

因为:

编译器知道 pPerson


let g: Greeter = Person()
g.greet()

❗ 输出:

Hello from protocol extension

🔥 坑就在这

为什么?

  • g 的静态类型是 Greeter
  • greet() 不在 protocol 声明中
  • 编译期直接绑定 extension 实现
  • Person 的实现完全被忽略

四、正确参与多态的写法(必须这样)

✅ 在 protocol 中声明

protocol Greeter {
    func greet()
}

extension Greeter {
    func greet() {
        print("Hello from default implementation")
    }
}

struct Person: Greeter {
    func greet() {
        print("Hello from Person")
    }
}

调用

let g: Greeter = Person()
g.greet()

✅ 输出:

Hello from Person

因为:

  • 方法在协议中
  • witness table 生效
  • 动态派发

五、技术本质(深入一点)

Swift 的两种派发路径

情况派发方式
concrete type 调用静态派发 / 直接调用
protocol 要求witness table
protocol extension 新方法静态派发
class overridevtable

为什么 Swift 要这么设计?

👉 性能 + 可预测性

  • extension 方法本质是“语法糖的 free function”
  • 不参与协议要求,避免额外表项
  • 让“工具方法 / helper”更高效

Swift 团队是有意为之,不是 bug。


六、工程级使用原则(非常重要)

🟢 适合写在 protocol extension 里的方法

  • 工具方法
  • 模板方法
  • 不需要被 override 的行为
  • 基于协议已有能力的派生行为
extension Collection {
    func isNotEmpty() -> Bool {
        !isEmpty
    }
}

🔴 不要写在 extension 里的方法

❌ 你希望子类型“自定义实现”的方法

// ❌ 错误设计
extension Cache {
    func clear() { ... }
}

✅ 正确方式

protocol Cache {
    func clear()
}

extension Cache {
    func clear() {
        print("default clear")
    }
}

七、一个 Swift 架构口诀(记住这个)

要多态 → 写进 protocol
要复用 → 写进 extension
extension 里的新方法 ≠ 协议要求


八、常见误解澄清

❓“那 extension 方法是不是没用?”

❌ 完全不是!

它们非常重要:

  • 默认行为
  • 算法组合
  • 语义增强(如 Collection
  • 性能友好

只是你不能指望它像 OOP override 那样工作


九、一句话终极总结

protocol extension 的方法是“静态绑定的能力补充”,不是“可重写的多态接口”。