从Swift 4中的结构获取所有关键路径

Rod*_*uiz 12 swift swift4

假设我有这个结构:

struct MyStruct {
    let x: Bool
    let y: Bool
}
Run Code Online (Sandbox Code Playgroud)

在Swift 4中,我们现在可以使用myStruct[keyPath: \MyStruct.x]界面访问它的属性.

我需要的是一种访问所有关键路径的方法,例如:

extension MyStruct {

    static func getAllKeyPaths() -> [WritableKeyPath<MyStruct, Bool>] {
        return [
            \MyStruct.x,
            \MyStruct.y
        ]
    }

}
Run Code Online (Sandbox Code Playgroud)

但是,显然,没有我必须手动声明数组中的每个属性.

我怎样才能做到这一点?

rra*_*ael 5

免责声明:

请注意,以下代码仅用于教育目的,不应在实际应用程序中使用,如果KeyPath以这种方式使用,可能会包含许多错误/奇怪的行为。

回答:

我不知道你的问题今天是否仍然相关,但挑战很有趣:)

这实际上可以使用镜像 API 实现。

KeyPath API 目前不允许我们从字符串初始化新的 KeyPath,但它确实支持字典“解析”。

这里的想法是构建一个字典来描述struct使用镜像 API,然后迭代键来构建 KeyPath 数组。

Swift 4.2 游乐场:

protocol KeyPathListable {
  // require empty init as the implementation use the mirroring API, which require
  // to be used on an instance. So we need to be able to create a new instance of the 
  // type.
  init()

  var _keyPathReadableFormat: [String: Any] { get }
  static var allKeyPaths: [KeyPath<Foo, Any?>] { get }
}

extension KeyPathListable {
  var _keyPathReadableFormat: [String: Any] {
    let mirror = Mirror(reflecting: self)
    var description: [String: Any] = [:]
    for case let (label?, value) in mirror.children {
      description[label] = value
    }
    return description
  }

  static var allKeyPaths: [KeyPath<Self, Any?>] {
    var keyPaths: [KeyPath<Self, Any?>] = []
    let instance = Self()
    for (key, _) in instance._keyPathReadableFormat {
      keyPaths.append(\Self._keyPathReadableFormat[key])
    }
    return keyPaths
  }
}

struct Foo: KeyPathListable {
  var x: Int
  var y: Int
}

extension Foo {
  // Custom init inside an extension to keep auto generated `init(x:, y:)`
  init() {
    x = 0
    y = 0
  }
}

let xKey = Foo.allKeyPaths[0]
let yKey = Foo.allKeyPaths[1]

var foo = Foo(x: 10, y: 20)
let x = foo[keyPath: xKey]!
let y = foo[keyPath: yKey]!

print(x)
print(y)
Run Code Online (Sandbox Code Playgroud)

请注意,打印输出的顺序并不总是相同(可能是因为镜像 API,但对此不太确定)。

  • 我盯着这种方式看了太久,没有意识到你回答了字面问题,但没有回答问题的精神。不过很聪明。对于那些想知道的人:KeyPath 正在索引到用 Mirror 构造的 Dictionary - 它们不是对结构属性的读/写 KeyPath。 (4认同)
  • 在 Swift 4.2 中,枚举字典保证是随机顺序,在应用程序的不同运行中是不同的。所以这可能就是你得到不同顺序结果的原因。 (2认同)

Por*_*ild 5

在修改了 rraphael 的回答后,我在 Swift 论坛上询问了这个问题。

有可能,这里讨论:

使用 Mirror 自动获取成员的 KeyPath

此外,Swift for TensorFlow 团队已经将其内置到 Swift for TensorFlow 中,这可能会成为纯粹的 Swift:

使用键路径的动态属性迭代