如何将AnyKeyPath转换为带可选类型的ReferenceWritableKeyPath?
背景情境
考虑以下类和KeyPath实例:
class Foo { var name: String? = "" } var foo = Foo() let keyPath: ReferenceWritableKeyPath<Foo, String?> = \.name let keyPaths: [AnyKeyPath] = [keyPath]
问题
现在我们有一个经过类型擦除的AnyKeyPath集合,其本质都是ReferenceWritableKeyPath(AnyKeyPath的rootType和valueType均已知)。
我希望将AnyKeyPath转换回ReferenceWritableKeyPath,并通过它执行类似foo.name = nil的赋值操作。目前实现的写法非常繁琐,以下是我当前的代码:
func tryWrite<Base>(_ newValue: Any?, to: inout Base, through: AnyKeyPath, withKnownKeyPathValueType kpValueType: Any) { return _tryWrite(newValue, to: &to, through: through, withKnownKeyPathValueType: kpValueType) } func _tryWrite<Base, Value>(_ newValue: Any?, to: inout Base, through: AnyKeyPath, withKnownKeyPathValueType: Value) { guard let kp = through as? ReferenceWritableKeyPath<Base, Value> else { print("failed to cast keypath") return } to[keyPath: kp] = (newValue as! Value) } tryWrite(nil, to: &foo, through: keyPaths.first!, withKnownKeyPathValueType: Optional(""))
这段代码可以运行,但肯定有更优的实现方式。请问更好的方法是什么?
背景说明
这是一个简化示例。实际场景中,我正在实现一个类似SwiftData的数据框架中的“删除规则”,定义方式如下:
@Relationship(deleteRule: .nullify, inverse: \Bar.blah) var children: [Bar] = []
在删除操作期间,我需要合并所有需要置空引用的对象,因此得到了[AnyKeyPath]集合,需要将它们转换回ReferenceWritableKeyPath。
因此必须使用KeyPath来实现,不能采用其他替代方案(比如捕获闭包)。
优化方案
方法1:基于已知类型信息的直接转换
利用AnyKeyPath自带的rootType和valueType属性,结合泛型约束简化转换逻辑,无需额外传递类型占位符:
func setValue<Base>(_ newValue: Any?, for base: inout Base, through keyPath: AnyKeyPath) { // 验证根类型匹配 guard keyPath.rootType == Base.self else { print("KeyPath根类型不匹配") return } // 尝试转换为可写KeyPath并赋值 guard let writableKP = keyPath as? ReferenceWritableKeyPath<Base, Any>, let typedValue = newValue as? Any else { print("KeyPath转换或值类型转换失败") return } base[keyPath: writableKP] = typedValue } // 使用示例 setValue(nil, for: &foo, through: keyPaths.first!)
方法2:类型安全的扩展封装
给AnyKeyPath添加扩展,封装类型转换逻辑,提升代码可读性和复用性:
extension AnyKeyPath { func asReferenceWritable<Base, Value>() -> ReferenceWritableKeyPath<Base, Value>? { guard self.rootType == Base.self, self.valueType == Value.self else { return nil } return self as? ReferenceWritableKeyPath<Base, Value> } } // 使用示例 if let kp = keyPaths.first?.asReferenceWritable() as ReferenceWritableKeyPath<Foo, String?>? { foo[keyPath: kp] = nil }
方法3:针对引用类型的简化写法
由于ReferenceWritableKeyPath仅适用于引用类型,可省略inout参数,直接通过对象实例修改属性:
func setValueForReferenceType(_ newValue: Any?, for base: AnyObject, through keyPath: AnyKeyPath) { guard let writableKP = keyPath as? ReferenceWritableKeyPath<AnyObject, Any>, let typedValue = newValue as? Any else { print("转换失败") return } base[keyPath: writableKP] = typedValue } // 使用示例 setValueForReferenceType(nil, for: foo, through: keyPaths.first!)
内容的提问来源于stack exchange,提问作者Bryan
相关产品推荐
相关产品推荐

