KeyPath 的用途是什么?

What is a KeyPath used for?

在 Swift 4 中,基金会团队的许多人讨论了与 Swift 3 相比,使用 keyPaths 要容易得多。这引出了一个问题……什么是 keyPath?说真的,我找不到任何明确的资源。

Objective-C 能够动态引用 属性 而不是直接引用。这些引用称为键路径。它们与直接 属性 访问不同,因为它们实际上并不读取或写入值,它们只是将其存储起来以备使用。

让我们定义一个名为 Cavaliers 的结构和一个名为 Player 的结构,然后为每个创建一个实例:

// an example struct
struct Player {
    var name: String
    var rank: String
}

// another example struct, this time with a method
struct Cavaliers {
    var name: String
    var maxPoint: Double
    var captain: Player

    func goTomaxPoint() {
        print("\(name) is now travelling at warp \(maxPoint)")
    }
}

// create instances of those two structs
let james = Player(name: "Lebron", rank: "Captain")
let irving = Cavaliers(name: "Kyrie", maxPoint: 9.975, captain: james)

// grab a reference to the `goTomaxPoint()` method
let score = irving.goTomaxPoint

// call that reference
score()

最后几行创建了对名为 score 的 goTomaxPoint() 方法的引用。问题是,我们无法创建对船长姓名 属性 的引用,但 keypath 可以。

let nameKeyPath = \Cavaliers.name
let maxPointKeyPath = \Cavaliers.maxPoint
let captainName = \Cavaliers.captain.name
let cavaliersName = irving[keyPath: nameKeyPath]
let cavaliersMaxPoint = irving[keyPath: maxPointKeyPath]
let cavaliersNameCaptain = irving[keyPath: captainName]

请使用 Xcode 9 或可用的快照进行测试。

Swift KVC

[Objective-C KVC]

KeyPath 是对 属性 类型的 reference 而不是 。它为语言增添了活力

其中有一些:

  • KeyPath<Root, Value> - 只读
  • WritableKeyPath<Root, Value> - read/write 对于 var 属性
  • ReferenceWritableKeyPath<Root, Value> - read/write 仅适用于引用类型[About]
  • PartialKeyPath<Root>
  • AnyKeyPath

你会发现 KeyPath 用于快捷方式(例如排序、迭代、过滤)、KVO[Example], MemoryLayout[Example]、SwiftUI 和其他更高级的功能

语法

class SomeClass {
    var v: String = "Hello World"
}

Pre Swift v4 - 速度慢且类型不安全

@objc //For example var v: String = "Hello world"
let keyPath = #keyPath(SomeClass.v) //keyPath is String Type
//read
someClass.value(forKeyPath: keyPath) //or forKey:
//write
someClass.setValue("Another string", forKeyPath: keyPath) //or forKey:
  • 从反斜杠开始 \
let someKeyPath = \SomeClass.v //KeyPath<SomeClass, String>
  • 如果有已知对象可以使用\.
someClass.observe(\.v, options: .new) //someClass1.v
//read
let res = someClass[keyPath: \SomeClass.v]

//write
someClass[keyPath: \.v] = "Another string"