Swift Dictionary非下标Getter实现:自定义下标重写规避递归问题
问题
我正尝试为自定义Dictionary类型重写默认下标方法,要求特定键不能存储特定类型的值,因此需要在下标setter中控制值的允许/拒绝。(这可能属于XY Problem,我也关注替代方案,但当前问题仍需解答)
下标扩展的setter部分运行正常,使用updateValue(_:forKey:)替代下标setter避免了无限递归。代码如下:
struct MyData { var isBadData: Bool } extension Dictionary where Key == String, Value == MyData { subscript(key: Key) -> Value? { // 省略getter会报错:Subscript with a setter must also have a getter get { return self[key] // 警告:函数调用会导致无限递归 // 问题:是否存在非下标getter替代方案? } set(newValue) { if let newValue, newValue.isBadData { return // 不向字典中添加坏数据 } else if let newValue { //self[key] = newValue // 警告:函数调用会导致无限递归 updateValue(newValue, forKey: key) // 非下标setter替代方案可行 } else { removeValue(forKey: key) } } } }
但对于getter,我不知道如何不递归调用下标getter来获取字典中的值。NSDictionary有value(forKey:)方法,但Swift Dictionary文档中似乎没有类似的非下标替代方法。如何不使用下标获取Dictionary的值?或者有没有办法调用下标原实现的super方法?
解决方案
绕开递归的Getter实现
可以使用first(where:)方法遍历字典匹配键值,避免调用自定义下标:
get { return first(where: { $0.key == key })?.value }
这种方式通过遍历查找值,小字典场景下性能足够,但大字典的效率会低于原生下标。
更稳妥的替代方案(解决XY Problem)
重写系统Dictionary的下标容易引发递归、依赖底层实现等问题,更推荐封装自定义结构体包裹字典,完全控制存取逻辑:
struct FilteredDictionary { private var storage: [String: MyData] = [:] subscript(key: String) -> MyData? { get { return storage[key] } set(newValue) { guard let newValue = newValue else { storage.removeValue(forKey: key) return } if !newValue.isBadData { storage[key] = newValue } } } // 按需添加字典常用方法,比如: var keys: Dictionary<String, MyData>.Keys { storage.keys } var values: Dictionary<String, MyData>.Values { storage.values } var count: Int { storage.count } }
这种方式逻辑清晰,不存在递归风险,还能根据需求扩展更多自定义功能,是生产环境的优先选择。
内容的提问来源于stack exchange,提问作者pkamb
相关产品推荐
相关产品推荐

