简体   繁体   English

Swift 5 存储和传递 KeyPath

[英]Swift 5 storing and passing KeyPaths

Let's say I have the following class:假设我有以下课程:

class User: NSObject {
  var name = "Fred"
  var age = 24
  var email = "fred@freddy.com"
  var married = false
}

I want to be able to write a generic function that takes in a list of KeyPath s for a known class type, read the values and print to screen.我希望能够编写一个通用函数,该函数接受已知类类型的KeyPath列表,读取值并打印到屏幕。 The problem is, the I can't get the following code to compile as the type of the KeyPath 's Value is not known, and will be different for each time.问题是,我无法编译以下代码,因为KeyPathValue的类型未知,并且每次都会不同。 What do I have to do to make this work generically?我必须做什么才能使这项工作普遍有效?

Consider the following:考虑以下:

struct KeyPathProperties<T> {
  var name: String
  var relatedKeyPaths: [KeyPath<T, Any>]
}

extension KeyPath where Root == User {
  var properties: KeyPathProperties<Root> {
    switch self {
      case \Root.name:
        return KeyPathProperties(name: "name", relatedKeyPaths: [\Root.age, \Root.email])
      default:
        fatalError("Unknown key path")
    }
  }
}

This line fails to compile:此行无法编译:

return KeyPathProperties(name: "name", relatedKeyPaths: [\Root.age, \Root.email])

with this error:出现此错误:

Cannot convert value of type 'KeyPath<User, Int>' to expected element type 'KeyPath<User, Any>'

This is what I wish to be able to do, for instance:这就是我希望能够做的,例如:

let myUser = User()

var keyPathProps = KeyPathProperties(name: "name", relatedKeyPaths: [\User.age, \User.email])

for keyPath in props.relatedKeyPaths {
  print("Value: \(myUser[keyPath: keyPath])")
}

The above won't compile of course.以上当然不会编译。 Essentially I want to store keyPaths in an array at runtime, so I can generically at some point in time get values out of the User .基本上我想在运行时将 keyPaths 存储在一个数组中,所以我可以在某个时间点从User获取值。 I need to know if I can re-write the above in some way where the compiler can safely and correctly determine the type of the keyPath's value at runtime.我需要知道我是否可以以某种方式重写上述内容,编译器可以在运行时安全且正确地确定 keyPath 值的类型。

This is a conceptual use case for a much more complex architectural issue I'm trying to solve with hopefully less code.这是一个更复杂的架构问题的概念用例,我正试图用更少的代码来解决。

MORE INFORMATION:更多信息:

At runtime I wish to keep track of the properties that get modified - these properties are held in a modifiedProps array in each object / instance.在运行时,我希望跟踪被修改的属性 - 这些属性保存在每个对象/实例的 modifiedProps 数组中。 At some point at runtime, I wish to be able to enumerate over this array of KeyPaths and print their values like so:在运行时的某个时候,我希望能够枚举这个 KeyPath 数组并像这样打印它们的值:

for modifiedKeyPath in self.modifiedProps { 
  print ("\(self[keyPath: modifiedKeyPath])" 
}

In short - I need to be able to capture the generic type of the KeyPath within KeyPathProperties .简而言之 - 我需要能够在KeyPathProperties捕获 KeyPath 的通用类型。 How do I achieve this?我如何实现这一目标?

SIDE NOTE: I can already easily achieve this by using Swift 3 style string based KeyPaths (by adding @objc to the class properties).边注:通过使用基于 Swift 3 样式字符串的 KeyPaths(通过将@objc添加到类属性),我已经可以轻松实现这一点。 I can store an array of keyPaths as strings and later do:我可以将 keyPaths 数组存储为字符串,然后执行以下操作:

let someKeyPath = #keyPath(User.email)
...

myUser.value(forKeyPath: someKeyPath)

I just cannot do this with Swift 4 KeyPaths generically.我只是不能用 Swift 4 KeyPaths 一般地做到这一点。

The error tells you what your misconception is:该错误告诉您您的误解是什么:

Cannot convert value of type 'KeyPath<User, Int>' 
    to expected element type 'KeyPath<User, Any>'

You seem to think that you can use a KeyPath<User, Int> where a KeyPath<User, Any> is expected, ostensibly on the grounds that an Int is an Any.您似乎认为您可以在需要KeyPath<User, Any>地方使用KeyPath<User, Int> ,表面上是因为 Int 是 Any。 But that's not true.但事实并非如此。 These are generic types, and generic types are not covariant — that is, there is no substitution principle for generics based on their parameterized types.这些是泛型类型,泛型类型不是协变的——也就是说,没有基于参数化类型的泛型替换原则。 The two types are effectively unrelated.这两种类型实际上是不相关的。

If you need an array of key paths regardless of their parameterized types, you would need an array of PartialKeyPath or AnyKeyPath.如果您需要一组键路径而不考虑其参数化类型,则需要一个 PartialKeyPath 或 AnyKeyPath 数组。 It seems that in your use case the root object is the same throughout, so presumably you want PartialKeyPath.在您的用例中,根对象似乎始终相同,因此您可能需要 PartialKeyPath。

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

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