存储异构键路径数组
Storing array of heterogeneous keypaths
我有一个对象类似于:
class MyObject {
var title:String? = nil
var value:Int? = nil
var otherData:[String] = []
}
我正在尝试存储一个异构的更新数组,以便稍后可以使用它们来设置值。像这样。
class SomeFormThing {
func getUpdates() -> [WritableKeyPath<MyObject, Any> : Any] {
var updates:[WritableKeyPath<MyObject, Any> : Any]
updates[\MyObject.title] = "New Title"
updates[\MyObject.value] = 0
updates[\MyObject.value] = ["Other", "Values"]
return updates
}
}
class SyncManager {
var form:SomeFormThing = SomeFormThing()
var sync:MyObject = MyObject()
func updateValues() {
let updates = form.getUpdates()
for key in updates.keys {
sync[forKeyPath: key] = updates[key]
}
}
}
然而,这当然在 getUpdates() 中读取 Cannot convert value of type 'WritableKeyPath<MyObject, String?>' to expected argument type 'WritableKeyPath<MyObject, Any>'
的行上存在编译问题。
需要有一个异构可写键路径数组,然后我可以用它来设置值。考虑到泛型通常是如何工作的,我不明白为什么 String 不能等同于 Any 泛型,因为类型是向下转换的。
Swift collection不喜欢异类。此外,键路径是泛型,为了类型安全,泛型不是协变的。
所以你不能将不同的类型组合成一个 collection — 除非你 type-erase。当然,如果你那样做,你必须稍后 un 键入擦除,这很痛苦。这就像蟑螂汽车旅馆:包装类型进去了,但除非你用手引出它们,否则它们不会出来。
例如:
struct MyObject {
var title:String
var value:Int
}
// okay, roaches go in...
let kp1 = \MyObject.title
let kp2 = \MyObject.value
let dict : [PartialKeyPath<MyObject>:Any] = [kp1:"Yo", kp2:1]
var object = MyObject(title: "Ha", value: 0)
// but now what? how can we apply our `dict` entries to `object`? well...
for (kp,val) in dict {
switch (kp,val) {
case (let kp2 as WritableKeyPath<MyObject,String>, let val2 as String):
object[keyPath:kp2] = val2
case (let kp2 as WritableKeyPath<MyObject,Int>, let val2 as Int):
object[keyPath:kp2] = val2
default:break
}
}
print(object) // yep, we did it, sort of
所以你看,你可以做到,但你必须单独处理每一种可能的 属性 类型。您已经在 PartialKeyPath 的 Partial 和 Any 中隐藏了类型,现在是否将它们强制打开由您决定。
就是说,我实际这样做的方法是为 MyObject 提供一个通用的 update
方法,并且一次只调用它一个变化:
struct MyObject {
var title:String
var value:Int
mutating func update<T>(_ kp:WritableKeyPath<Self,T>, to val:T) {
self[keyPath:kp] = val
}
}
等等:
object.update(\.title, to: "Teehee")
object.update(\.value, to: 42)
请注意,您不能直接从 dict
中的条目执行此操作,因为您已经隐藏了类型。泛型不会等到运行时才去查看“真正”的东西是什么类型;它由编译器解析,因此类型必须是已知的——通过说 PartialKeyPath 和 Any 你已经使类型 un 已知。
我有一个对象类似于:
class MyObject {
var title:String? = nil
var value:Int? = nil
var otherData:[String] = []
}
我正在尝试存储一个异构的更新数组,以便稍后可以使用它们来设置值。像这样。
class SomeFormThing {
func getUpdates() -> [WritableKeyPath<MyObject, Any> : Any] {
var updates:[WritableKeyPath<MyObject, Any> : Any]
updates[\MyObject.title] = "New Title"
updates[\MyObject.value] = 0
updates[\MyObject.value] = ["Other", "Values"]
return updates
}
}
class SyncManager {
var form:SomeFormThing = SomeFormThing()
var sync:MyObject = MyObject()
func updateValues() {
let updates = form.getUpdates()
for key in updates.keys {
sync[forKeyPath: key] = updates[key]
}
}
}
然而,这当然在 getUpdates() 中读取 Cannot convert value of type 'WritableKeyPath<MyObject, String?>' to expected argument type 'WritableKeyPath<MyObject, Any>'
的行上存在编译问题。
需要有一个异构可写键路径数组,然后我可以用它来设置值。考虑到泛型通常是如何工作的,我不明白为什么 String 不能等同于 Any 泛型,因为类型是向下转换的。
Swift collection不喜欢异类。此外,键路径是泛型,为了类型安全,泛型不是协变的。
所以你不能将不同的类型组合成一个 collection — 除非你 type-erase。当然,如果你那样做,你必须稍后 un 键入擦除,这很痛苦。这就像蟑螂汽车旅馆:包装类型进去了,但除非你用手引出它们,否则它们不会出来。
例如:
struct MyObject {
var title:String
var value:Int
}
// okay, roaches go in...
let kp1 = \MyObject.title
let kp2 = \MyObject.value
let dict : [PartialKeyPath<MyObject>:Any] = [kp1:"Yo", kp2:1]
var object = MyObject(title: "Ha", value: 0)
// but now what? how can we apply our `dict` entries to `object`? well...
for (kp,val) in dict {
switch (kp,val) {
case (let kp2 as WritableKeyPath<MyObject,String>, let val2 as String):
object[keyPath:kp2] = val2
case (let kp2 as WritableKeyPath<MyObject,Int>, let val2 as Int):
object[keyPath:kp2] = val2
default:break
}
}
print(object) // yep, we did it, sort of
所以你看,你可以做到,但你必须单独处理每一种可能的 属性 类型。您已经在 PartialKeyPath 的 Partial 和 Any 中隐藏了类型,现在是否将它们强制打开由您决定。
就是说,我实际这样做的方法是为 MyObject 提供一个通用的 update
方法,并且一次只调用它一个变化:
struct MyObject {
var title:String
var value:Int
mutating func update<T>(_ kp:WritableKeyPath<Self,T>, to val:T) {
self[keyPath:kp] = val
}
}
等等:
object.update(\.title, to: "Teehee")
object.update(\.value, to: 42)
请注意,您不能直接从 dict
中的条目执行此操作,因为您已经隐藏了类型。泛型不会等到运行时才去查看“真正”的东西是什么类型;它由编译器解析,因此类型必须是已知的——通过说 PartialKeyPath 和 Any 你已经使类型 un 已知。