一、场景说明:默认实现破坏多态
背景
假设你在写一个 缓存协议 Cache,希望:
- 提供默认实现
clear() - 不同缓存类型(MemoryCache / DiskCache)可以覆盖
1️⃣ 协议 + 默认实现(错误写法)
protocol Cache {}
extension Cache {
func clear() {
print("default clear")
}
}
struct MemoryCache: Cache {
func clear() {
print("MemoryCache cleared")
}
}
struct DiskCache: Cache {
func clear() {
print("DiskCache cleared")
}
}
2️⃣ 不同调用方式导致的 bug
let memCache = MemoryCache()
memCache.clear()
// 输出: MemoryCache cleared ✅
let cache: Cache = MemoryCache()
cache.clear()
// 输出: default clear ❌ 预期 MemoryCache cleared
原因:
clear()没有在 protocol 中声明 → 不进入 witness table- protocol 类型调用时走 静态派发 → 调用 extension 默认实现
- MemoryCache 的实现被忽略 → 多态失效
🔥 这就是典型的默认实现导致 bug的场景。
二、规避策略
✅ 1️⃣ 在 protocol 中声明方法(标准做法)
protocol Cache {
func clear() // 声明多态点
}
extension Cache {
func clear() { // 默认实现
print("default clear")
}
}
struct MemoryCache: Cache {
func clear() { // 覆盖默认实现
print("MemoryCache cleared")
}
}
struct DiskCache: Cache {
func clear() {
print("DiskCache cleared")
}
}
let cache: Cache = MemoryCache()
cache.clear()
// 输出: MemoryCache cleared ✅
- 核心:多态点必须在协议中声明
- 默认实现仍然保留 → 类型可以选择使用或覆盖
✅ 2️⃣ 对于不想覆盖的方法 → 只写 extension(辅助工具)
protocol Cache {
func clear()
}
extension Cache {
func logClear() { // 辅助方法
print("Clearing cache...")
}
}
logClear()不在 protocol 声明中 → 静态派发- 只做工具函数,不破坏多态
✅ 3️⃣ 多协议继承时谨慎使用默认实现
protocol Logger {}
protocol FileLogger: Logger {}
extension Logger {
func log() { print("Logger log") }
}
extension FileLogger {
func log() { print("FileLogger log") }
}
struct MyLogger: FileLogger {}
let l1: Logger = MyLogger()
l1.log() // Logger log
let l2: FileLogger = MyLogger()
l2.log() // FileLogger log
⚠️ 同一个对象,输出不同 → 静态派发陷阱
规避:
- 把
log()声明在协议中 - extension 只提供默认实现
- conforming 类型覆盖 → 多态恢复
三、总结工程级经验
| 场景 | 问题 | 解决方案 |
|---|---|---|
| 默认实现不在协议中 | protocol 类型调用失效 → 多态被破坏 | 把方法声明在 protocol 中 |
| 多协议 extension 同名方法 | 静态派发导致不同输出 | 避免同名方法,或明确协议声明 + 默认实现 |
| 默认实现覆盖不了类型自实现 | 不是 bug,而是静态派发行为 | 遵循“多态点必须在协议中声明” |
四、黄金口诀
“想让子类型可重写 → 方法必须声明在 protocol 本体;
协议扩展默认实现 = 可选默认行为,不能依赖它实现多态。”