一、先给一句“人话版”结论
协议扩展中的方法,是否参与多态,取决于:
它有没有在协议本身中声明。
- ❌ 只写在
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
因为:
编译器知道
p是Person
let g: Greeter = Person()
g.greet()
❗ 输出:
Hello from protocol extension
🔥 坑就在这
为什么?
g的静态类型是Greetergreet()不在 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 override | vtable |
为什么 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 的方法是“静态绑定的能力补充”,不是“可重写的多态接口”。