繁体   English   中英

存储异构键路径数组

[英]Storing array of heterogeneous keypaths

我有一个 object 类似于:

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>'

需要有一组异构的可写键路径,然后我可以使用它们来设置值。 鉴于 generics 通常如何工作,我不明白为什么 String 不能等同于 Any 泛型,因为该类型是向下转换的。

Swift collections 不喜欢异类。 此外,为了类型安全,键路径是 generics,而 generics 不是协变的。

所以你不能在一个集合中组合不同的类型——除非你类型擦除。 当然,如果你这样做,你必须稍后取消键入擦除,这是一个痛苦的屁股。 这就像一个蟑螂汽车旅馆:包装类型 go 但除非您手动引出,否则它们不会出来。

例如:

    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 你已经使类型未知。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM