写点什么

Swift 语言中的 Key-Path 特性浅析

作者:珲少
  • 2024-10-30
    上海
  • 本文字数:1473 字

    阅读完需:约 5 分钟

Swift 语言中的 Key-Path 特性浅析

Key-Path 字面理解为键路径,熟悉 Objective-C 语言的同学知道,OC 中有一种语法叫做 KVC,即简直编码,其作用是允许开发者通过字符串路径来访问对象的属性,这也是 Objective-C 语言动态化的一种特性。总所周知,Swift 是一种强调静态类型与编码安全的语言,因此动态化本身在 Swift 语言中并不是一种受欢迎的特性,静态化的编程可以让更多的异常问题在编译时被检查出来,并且从开发直觉上来说,静态化也比动态化的代码逻辑更容易理解。


然而,动态化确实也可以代码极大的好处,尤其是一些特殊的应用场景中,动态化可以允许开发者在运行时决定要操作数据,可以使用简洁的代码编写出非常强大的功能逻辑。


回归正题,文本主要讨论 Swift 编程语言中的 Key-Path 特性,Key-Path 与 Objective-C 中的 KVC 有些类似,其为 Swift 增加了一种动态访问对象属性的能力,但对 KVC 的完全动态化又进行了一些限制,Swift 中定义了一种特殊的类型来描述对象键的路径,这就是 Key-Path。那么它有什么用?怎么用呢?这是本文要介绍的重点,希望能给你带来启发。

什么是 Key-Path

编程的本质是使用特定的语言来描述算法或数据。Key-Path 是 Swift 中内置的一种类型,定义如下:


class KeyPath<Root, Value>
复制代码


可以看到这个 Key-Path 类的定义关联了两个泛型,Root 描述的是支持此 Key-Path 的对象本身的类型,Value 描述的是此 Key-Path 对应的路径的属性的类型。这么讲可能有些抽象,我们通过一个小例子来看就比较清晰了。


// k是一个key-path对象let k: KeyPath<String, Int> = \String.count
let str = "Hello World"// 将读取到str的count属性let value = str[keyPath: k]// 11print(value)
复制代码


Key-Path 在定义时,语法是使用\符号,之后跟上对象类型与属性名,属性名是支持链式描述的。


自定义的类型也可以很方便的使用 Key-Path,例如:


struct Subject {    var name: String}
struct Student { var name: String}
struct Teacher { var name: String var age: Int var subject: Subject var students: [Student]}
let t = Teacher(name: "Teacher Wang", age: 34, subject: .init(name: "Math"), students: [.init(name: "Xiaoming"), .init(name: "Linlin")])// Teacher Wangprint(t[keyPath: \Teacher.name])// Mathprint(t[keyPath: \Teacher.subject.name])// Linlinprint(t[keyPath: \Teacher.students[1].name])
复制代码


可以看到,Key-Path 路径除了可以链式定义,还支持数组下标。另外,如果在上下文中是可以推断出 Key-Path 的 Root 泛型具体类型时,也可以省略对象的类型部分,例如:


// 34print(t[keyPath: \.age])
复制代码


在属性路径中也支持使用 self 来描述对象本身,例如:


// 当前Teacher对象本身:tprint(t[keyPath: \.self])
复制代码


如果路径中的某个属性是可选的,那么在定义 Key-Path 时,也可以类似可选链的方式进行拆包,如下:


class Class {    var t: Teacher?}
let c = Class()// nilprint(c[keyPath: \.t?.name])
复制代码

Key-Path 的一些应用

从语法层面上来看,Key-Path 的写法非常简洁,在某些场景下,我们可以用其来代替闭包或函数。最常见的应用场景为 filter、map 这些方法。例如:


let teachers = [Teacher(name: "A", age: 30, subject: .init(name: "A"), students: []),                Teacher(name: "B", age: 20, subject: .init(name: "B"), students: []),                Teacher(name: "C", age: 33, subject: .init(name: "C"), students: [])]// 会将所有Teacher的name map出来let names = teachers.map(\.name)// ["A", "B", "C"]print(names)
复制代码


发布于: 刚刚阅读数: 3
用户头像

珲少

关注

还未添加个人签名 2022-07-26 加入

还未添加个人简介

评论

发布
暂无评论
Swift语言中的Key-Path特性浅析_珲少_InfoQ写作社区