题目
设计一个同时支持值类型和引用类型的缓存协议
信息
- 类型:问答
- 难度:⭐⭐
考点
协议设计,值类型与引用类型,泛型约束,内存管理
快速回答
设计一个缓存协议需考虑:
- 使用
associatedtype定义泛型Key和Value - 使用
mutating修饰值类型的修改方法 - 通过
where子句约束Key为Hashable - 引用类型实现时省略
mutating - 通过
AnyCache类型擦除器统一处理
原理说明
Swift中协议需要同时支持结构体(值类型)和类(引用类型)时,核心挑战在于:
- 值类型方法修改自身需要
mutating关键字 - 引用类型不需要
mutating - 协议中无法同时声明带和不带
mutating的相同方法
协议设计与实现
基础协议定义:
protocol CacheProtocol {
associatedtype Key: Hashable
associatedtype Value
mutating func set(_ value: Value, forKey key: Key)
func get(forKey key: Key) -> Value?
}值类型实现(结构体):
struct MemoryCache<K: Hashable, V>: CacheProtocol {
private var storage: [K: V] = [:]
mutating func set(_ value: V, forKey key: K) {
storage[key] = value
}
func get(forKey key: K) -> V? {
return storage[key]
}
}引用类型实现(类):
class PersistentCache<K: Hashable, V>: CacheProtocol {
private var storage: [K: V] = [:]
// 注意:这里不需要 mutating
func set(_ value: V, forKey key: K) {
storage[key] = value
saveToDisk() // 引用类型特有的持久化操作
}
func get(forKey key: K) -> V? {
return storage[key]
}
private func saveToDisk() { /* ... */ }
}类型擦除统一处理
由于协议包含关联类型,需使用类型擦除器:
struct AnyCache<K: Hashable, V>: CacheProtocol {
private var _set: (V, K) -> Void
private var _get: (K) -> V?
init<T: CacheProtocol>(_ cache: T) where T.Key == K, T.Value == V {
var mutableCache = cache
_set = { value, key in
mutableCache.set(value, forKey: key)
}
_get = { key in
cache.get(forKey: key)
}
}
func set(_ value: V, forKey key: K) {
_set(value, key)
}
func get(forKey key: K) -> V? {
return _get(key)
}
}
// 使用示例
let memory: AnyCache<String, Int> = AnyCache(MemoryCache())
let disk: AnyCache<String, Int> = AnyCache(PersistentCache())最佳实践
- 优先使用
mutating声明协议方法,引用类型实现时可忽略该关键字 - 对Key使用
Hashable约束确保字典兼容性 - 通过类型擦除器隐藏具体实现类型
- 值类型实现时注意
inout参数传递的合规性
常见错误
- 在类实现中添加
mutating导致编译错误 - 忘记关联类型约束,导致无法使用字典存储
- 直接使用
CacheProtocol作为变量类型(需通过类型擦除) - 值类型的方法中修改自身但未标记
mutating
扩展知识
- Copy-on-Write(COW): 大型值类型推荐实现COW优化性能
- 内存管理: 引用类型需注意循环引用,可使用
weak引用 - 协议组合: 可组合
AnyObject约束实现类专属协议protocol ClassOnlyCache: CacheProtocol, AnyObject { /* ... */ }