Lifetime 与 Identity 的关系
View 的值跟 Identity 生命周期是不同的。值类型的 View 生命周期是非常短暂的。开发者要控制好的其实是它们的 Identity。也就是说,随着时间的推移, SwiftUI 创建很多新的 View 用来描述视图当前状态下的显示方式,但是 SwiftUI 内部只是拿这些 View 来进行样式和布局的对比,用完了这些 View 值就会销毁,其内部用 Identity 唯一标识的那个视图(RenderNode)会一直在内存中,并且一直都是同一个。但是一旦 Identity 发生变化,内部的视图元素生命周期也会结束。
所以最终我们得出如下公式来阐述 View,LifeTime,Identity 三者之间的关系:
-
- View Value ≠ View Identity
-
- View(视图)'s LifeTime = duration of the Identity
Lifetime 与 State 的关系
当一个视图根据 Identity 第一次创建的时候,SwiftUI 在内部为 State 和 StateObject 的 Storage 分配相应的内存空间,用来保存状态的初始值。注意这里的 Storage 跟 Identity 是对应的,生命周期也是一致的。View Identity 一旦变化,视图内部对应的数据状态也会被重新替换。也就是说:
- State's Lifetime = 视图's Lifetime != View's Lifetime
稳定的 Identity
在 Swift 标准库有个 Identifiable 协议来帮助开发者保证 Identity 稳定。SwiftUI 也充分利用了这个协议,使得开发者只需要提供 KeyPath,它内部通过 Identifiable 协议可以动态的访问到相应的属性,从而生成稳定的 View Identity。
如上图,如果仔细看下 ForEach 控件初始化函数的定义,可以看出 SwiftUI 充分利用了 Swift 类型系统的特性来约束 API 使用体验:
- 通过这个定义,能一眼看出 ForEach 声明了一个数据集合和一个视图集合之间的关系
- 将集合中的元素限制为必须遵循 Identifiable 协议,目的是为了保证集合元素能够提供一个稳定的 Identity,以便 SwiftUI 可以在视图的整个生命周期内跟踪数据。
所以,确保 Identity 的稳定性,对于开发者来说是非常重要的。因为他会影响到视图和与之对应数据的生命周期。
改进 Identity
Identity 就是依赖关系图的灵魂。正如之前所说,Identity 用来标识一个视图,所以 SwiftUI 会根据 Identity 来高效的判断哪些视图需要更新,哪些视图需要新建,哪些视图需要销毁。
稳定性
对于开发者来说,首先要确保的就是 Identity 的稳定性。
稳定的 Identity 会给 SwiftUI 带来如下好处:
- 确保视图生命周期的准确性,一个视图的生命周期是由 Identity 来决定的,一个不稳定的 Identity 会导致视图生命周期意外缩短
- 提高应用程序的性能,SwiftUI 无需在依赖关系图更新的过程中为不必要的视图和状态重新分配内存空间
- 缩小影响依赖关系影响的范围
- 保证数据状态不会无故丢失
唯一性
但是只保证 Identity 的稳定性还是不够的。好的 Identity 还要确保唯一性。每个 Identity 都应该准确映射到一个单一的视图。
唯一的 Identity 会给 SwiftUI 带来如下好处:
- 平滑的动画效果
- 同样可以提高性能
- 准确地的反应视图和状态之间的依赖关系
去分支
- 使用三目运算的方式来动态修改,去除if eles条件分支
总结
整篇文章的主角就是 Identity。我们介绍了 Identity 在 SwiftUI 中如何影响动画、生命周期以及相对应的状态。同时阐述了在视图更新的过程中,也需要 Identity 来帮助 SwiftUI 做决定。我们给出了很多正确使用 Identity 的范例,这将对提高 SwiftUI 应用程序的性能很有帮助。
下面我们来回答文章开头的问题:SwiftUI View 和 视图元素之间采用 Identity 关联起来,它们之间并非一一对应。在 SwiftUI 中每当状态发生变化,都会调用对应的 body 生成新的 View 值,但是否生成新的视图则完全由 Identity 来决定。如果 Identity 一致,就会根据 Identity 去内存中查找之前创建的视图,换言之,相当于保持之前视图的生命周期,并且在内存中用类维持住之前的数据状态,只对更改数据后,视图变化的部分进行渲染操作。如果 Identity 不一致,则会新建视图元素,同时视图所依赖的状态也会被重新分配,回到初始值。
总而言之,View Identity 对 SwiftUI 来说是至关重要的。我们一定要时刻注意 View 的 显式 Identity 和 结构性 Identity,并提高 Identity 的稳定性,确保 Identity 的唯一性。