0Pricing
Swift Academy · 课时

KeyPath 基础

将属性作为一等值进行引用。

KeyPath 基础 是 CoddyKit 上的免费 Swift Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Swift Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Swift Academy 课程共包含 4 节课。

什么是键路径

键路径是一种描述属性路径的值,但它本身并不会访问该属性。您可以把它理解为一个已存储且可复用的引用,用来表示您想要访问哪个属性,并且与从哪个实例读取它相互分离。

您可以使用反斜杠、类型名称、点号和属性名称来编写键路径。Swift 会将这个表达式赋予一个 KeyPath 值,您可以像传递其他值一样传递它。

struct Person {
    let name: String
    let age: Int
}

let nameKeyPath = \Person.name
print(type(of: nameKeyPath))

读取值

要通过键路径读取属性,请在实例上使用 [keyPath:] 下标。将键路径放在方括号中,Swift 就会返回该路径对应的值。

这相当于在运行时直接写出属性名称,但属性现在由数据选择,而不是硬编码。

struct Person {
    let name: String
    let age: Int
}

let alice = Person(name: "Alice", age: 30)
let path = \Person.name
print(alice[keyPath: path])

详细了解键路径语法

键路径的一般形式是反斜杠转义符、根类型、点号,然后是属性名称。反斜杠会告诉 Swift,您需要的是键路径,而不是属性的值。

根类型是路径的起始类型。结果类型是最终属性的类型。因此,从 Person 指向其 age 属性的路径,其根类型是 Person,值类型是 Int。

struct Person {
    let name: String
    let age: Int
}

let ageKeyPath: KeyPath<Person, Int> = \Person.age
let bob = Person(name: "Bob", age: 42)
print(bob[keyPath: ageKeyPath])

沿嵌套属性进行链接

键路径可以深入嵌套值。如果某个属性本身是一个结构体,您可以继续使用点号来访问更深层的属性。

整个链仍然是一个单独的键路径值,其根类型和结果类型分别反映链的起点和终点。

struct Address {
    let city: String
}
struct Person {
    let name: String
    let address: Address
}

let cityPath = \Person.address.city
let carol = Person(name: "Carol", address: Address(city: "Oslo"))
print(carol[keyPath: cityPath])

将键路径存储在变量中

由于键路径只是一个值,因此您可以将它存储在变量中、从函数返回它,或保存一个键路径集合。这样,您就可以在运行时决定要读取哪个属性。

这里我们根据一个标志,在两个已存储的键路径中选择一个。

struct Person {
    let name: String
    let age: Int
}

let showAge = true
let chosen: PartialKeyPath<Person> = showAge ? \Person.age : \Person.name
let dave = Person(name: "Dave", age: 27)
print(dave[keyPath: chosen])

PartialKeyPath 与 AnyKeyPath

Swift 提供了一组键路径类型。KeyPath<Root, Value> 同时知道路径的两端。PartialKeyPath<Root> 知道根类型,但会消除值类型。AnyKeyPath 则会同时消除两者。

保留的类型信息越多,编译器能为您检查的内容就越多。只有在确实需要混合不同值类型的路径时,才应使用部分类型或任意类型的形式。

struct Person {
    let name: String
    let age: Int
}

let paths: [PartialKeyPath<Person>] = [\.name, \.age]
let eve = Person(name: "Eve", age: 19)
for p in paths {
    print(eve[keyPath: p])
}

在已知类型内部使用简写

当根类型已经可以从上下文中确定时,您可以省略类型名称,只写反斜杠、点号和属性。编译器会推断根类型。

这种简写可以让调用位置更简洁,尤其是在元素类型明确的集合操作中。

struct Person {
    let name: String
    let age: Int
}

func value(of person: Person, at path: KeyPath<Person, Int>) -> Int {
    person[keyPath: path]
}

let frank = Person(name: "Frank", age: 55)
print(value(of: frank, at: \.age))

将键路径作为函数参数

一种常见模式是编写一个接受键路径的泛型函数,并将它应用于多个实例。这会将要读取的内容与执行读取操作的逻辑解耦。

这里有一个泛型辅助函数,可以从人员列表中提取任意类型的任意属性。

struct Person {
    let name: String
    let age: Int
}

func extract<T>(_ people: [Person], _ path: KeyPath<Person, T>) -> [T] {
    people.map { $0[keyPath: path] }
}

let people = [Person(name: "Ana", age: 21), Person(name: "Ben", age: 34)]
print(extract(people, \.name))
print(extract(people, \.age))

键路径与计算属性

只读键路径同样适用于计算属性。路径会捕获计算过程,因此通过它读取值时会运行 getter。

这意味着键路径并不局限于存储属性;任何可读取的属性都符合条件。

struct Rectangle {
    let width: Double
    let height: Double
    var area: Double { width * height }
}

let areaPath = \Rectangle.area
let r = Rectangle(width: 3, height: 4)
print(r[keyPath: areaPath])

身份键路径

有一种特殊的键路径指向值本身,写法是单独的反斜杠、点号和 self。通过它读取值时,会原样返回该实例。

当某个 API 需要键路径,而您想直接使用元素本身时,这种方式很方便,例如按数字自身进行排序。

let numbers = [3, 1, 2]
let identity = \Int.self
for n in numbers {
    print(n[keyPath: identity])
}

比较键路径

键路径符合 Hashable 和 Equatable。指向同一类型的同一属性的两个键路径相等,因此您可以将它们存储在集合中,或将它们用作字典键。

这对于构建将属性映射到标签或格式化器的表格很有用。

struct Person {
    let name: String
    let age: Int
}

let labels: [PartialKeyPath<Person>: String] = [
    \Person.name: "Full Name",
    \Person.age: "Years Old"
]
print(labels[\Person.age] ?? "unknown")

快速检查:通过键路径读取

检验您对如何通过键路径读取属性的理解。

回顾:键路径基础

您学习了键路径的基础知识:

  • 键路径由反斜杠、根类型、点号和属性名称组成,用来描述属性而不读取它。
  • 在实例上使用 [keyPath:] 下标读取值。
  • 路径可以链接到嵌套属性,也可以指向计算属性。
  • KeyPath、PartialKeyPath 和 AnyKeyPath 在类型安全性与灵活性之间进行取舍。
  • 键路径本身也是值:您可以存储、传递、比较它们,也可以将它们用作字典键。

接下来,您将使用键路径让集合操作变得简洁得多。

struct Person {
    let name: String
    let age: Int
}

let path = \Person.age
let gina = Person(name: "Gina", age: 48)
print("Read via key path: \(gina[keyPath: path])")

常见问题解答

「KeyPath 基础」课时是免费的吗?

是的 — 「KeyPath 基础」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Swift Academy 课程的其余内容,请升级到 CoddyKit PRO。 Swift Academy 课程共包含 4 节课。

「KeyPath 基础」这节课中我会学到什么?

将属性作为一等值进行引用。 你通过在浏览器中直接运行的动手代码来练习 Swift Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Swift Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Swift Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。

「KeyPath 基础」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Swift Academy 课中编写并运行代码吗?

能。每节 Swift Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. KeyPath 基础
  2. 在集合中使用 KeyPaths
  3. 可写键路径与引用键路径
  4. @dynamicMemberLookup
← 返回 Swift Academy