Swift 4:KeyPath

时间:2022-06-01 13:00:56

Swift 4对KeyPath做了很大的改进。改进后的为类型安全的KeyPath。

Swift 2的KeyPath

示例

class Person: NSObject {
  var name: String = ""

  init(name: String) {
    self.name = name
  }
}
let me = Person(name: "张三")
me.valueForKeyPath("name")

Swift 2的KeyPath为一个字符串。显然,使用字符串作为KeyPath很容易造成拼写错误。

Swift 3的KeyPath

鉴于使用字符串作为keypath的值很容易造成拼写错误,Swift 3新增了#keyPath()来增强KeyPath的类型安全。

class Person: NSObject {
  var name: String = ""

  init(name: String) {
    self.name = name
  }
}
let me = Person(name: "张三")
me.value(forKeyPath: #keyPath(Person.name))

Swift 4新的KeyPath

虽然Swift 3新增了#keyPath()来对KeyPath做类型检查,但是检查后返回的仍然是字符串。

Swift 3的KeyPath有以下几个缺点

  • 丢失了类型信息(因为#keyPath()返回的仍然是字符串,做KeyPath的所有类型都是Any)
  • 需要对#keyPath()检查后返回的字符串做没必要的解析
  • KeyPath只能应用于NSObject
  • 局限于Darwin平台

Swift 4新引入了反斜杠"\"来表示KeyPath,其语法为:

  • \<Type>.<path>
  • \.<path>

<Type>:类型名
<path>:由一个或以上的属性构成的路径链,即property.property.....

如果可以推断出KeyPath对应的类型可以使用点号"\."开头,省略类型名。

Smart KeyPaths: Better Key-Value Coding for Swift的示例

class Person {
    var name: String
    var friends: [Person] = []
    var bestFriend: Person? = nil
    init(name: String) {
        self.name = name
    }
}

var han = Person(name: "Han Solo")
var luke = Person(name: "Luke Skywalker")
luke.friends.append(han)

简单的KeyPath:Person.name

let hanName = han[keyPath:\.name]  //Han Solo

取得数组的KeyPath

let firstFriendsNameKeyPath = \Person.friends[0].name
let firstFriend = luke[keyPath: firstFriendsNameKeyPath] // "Han Solo"
// 等同于
luke[keyPath: \.friends[0].name] // "Han Solo"
//等同于
luke.friends[keyPath: \.[0].name] // "Han Solo"
//等同于
luke.friends[keyPath: \[Person].[0].name] // "Han Solo"

使用KeyPath修改属性值

// rename Luke's first friend
luke[keyPath: firstFriendsNameKeyPath] = "A Disreputable Smuggler"

可选属性的KeyPath

// optional properties work too
let bestFriendsNameKeyPath = \Person.bestFriend?.name
let bestFriendsName = luke[keyPath: bestFriendsNameKeyPath]  // nil, if he is the last Jedi

参考:https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md