在Swift 4中,Foundation团队的许多人讨论了与Swift 3相比使用keyPath更容易的程度。这引发了一个问题...什么是keyPath?说真的,我找不到任何清晰的资源。
在Swift 4中,Foundation团队的许多人讨论了与Swift 3相比使用keyPath更容易的程度。这引发了一个问题...什么是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()
最后几行创建了一个对goTomaxPoint()方法的引用,命名为score。问题在于,我们无法创建对船长姓名属性的引用,但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)与键值观察(KVO)]
KeyPath
(键路径)是对类型的属性的引用(而不是值)。它为语言增添了一种动态性。
以下是其中一些:
KeyPath<Root, Value>
- 只读WritableKeyPath<Root, Value>
- 对于var
属性可读/写ReferenceWritableKeyPath<Root, Value>
- 仅对引用类型可读/写[关于]PartialKeyPath<Root>
- 只读,Value可以是任意类型AnyKeyPath
- 只读,Root可以是任意类型,Value可以是任意类型KeyPath
由以下主要部分组成:Value Path、Root Type和Value Type(属性类型)
//Value Path is SomeClass.someVariable.count
//Root Type is SomeClass
//Value Type is Int as a result of .count
let customKeyPath: KeyPath<SomeClass, Int> = \SomeClass.someVariable.count
//read
let count1 = someClass1[keyPath: customKeyPath]
let count2 = someClass2[keyPath: customKeyPath]
KeyPath
用于快捷方式(例如排序、迭代、过滤)、KVO[示例]、MemoryLayout[示例]、SwiftUI 和其他更高级的功能。在动画中使用 #keyPath
,例如:let colorsAnimation = CABasicAnimation(keyPath: #keyPath(CAGradientLayer.colors))
示例:let arr: [SomeClass] = []
//sort from iOS v15
let result1 = arr.sorted(using: KeyPathComparator(\.someVariable, order: .reverse))
//map
let result2 = arr.map(\.someVariable)
//instead of arr.map { $0.someVariable }
语法:
class SomeClass {
@objc
var someVariable: String = "Hello World!"
}
let keyPath1: String = #keyPath(SomeClass.someVariable)
assert(keyPath1 == "someVariable")
//let keyPath2: KeyPath<SomeClass, String> = \SomeClass.someVariable
//let keyPath2: WritableKeyPath<SomeClass, String> = \SomeClass.someVariable
let keyPath2: ReferenceWritableKeyPath<SomeClass, String> = \SomeClass.someVariable
//let keyPath2: PartialKeyPath<SomeClass> = \SomeClass.someVariable
//let keyPath2: AnyKeyPath = \SomeClass.someVariable
let someClass = SomeClass()
//read
let res = someClass[keyPath: \SomeClass.someVariable] //or just \.v(from context)
//or
//let res = someClass[keyPath: keyPath2]
assert(res == "Hello World!")
//write only for writable KeyPath
//or you get build-time error:Cannot assign through subscript: 'keyPath2' is a read-only key path
someClass[keyPath: \SomeClass.someVariable] = "world is changed 1" //or just \.v(from context).
assert(someClass.someVariable == "world is changed 1")
someClass[keyPath: keyPath2] = "world is changed 2"
assert(someClass.someVariable == "world is changed 2")
Pre Swift v4 - slow and not type safe
//read
let res = someClass.value(forKeyPath: keyPath) //or forKey:
//write
someClass.setValue("Another string", forKeyPath: keyPath) //or forKey:
\
(反斜线)开始。let someKeyPath = \SomeClass.someVariable //KeyPath<SomeClass, String>
\.
(反斜杠点)someClass.observe(\.someVariable, options: .new) //someClass1.someVariable