前言
在SwiftUI当中,常见的传递数据模型的方式有@ObservedObject
和@EnvironmentObject
两种。有时候难以决断该用哪个?这里我凭自己的经验简单列举一些二者的异同,帮你抉择。
太长不看:需要传数据View的层级超过两层用EnvironmentObject,只有一层就用ObservedObject。
ObservedObject
- 常用于局部数据传递
比如在一个多层级的设置页面里,可以在设置的根页面创建一个
@StateObject private var model = Model()
// … 在子视图的构造函数里
SettingsChildView(model: model)
然后在设置的子视图SettingsChildView
里面通过@ObservedObject
来传参。
@ObservedObject var model: Model
因为传参的范围很小,所以无需超过整个设置页面的层级,也不会影响到其他的视图。
- 数据的所有权更加清晰
通过将数据模型声明在根视图上,整个数据传递的流向更清晰:所有的子视图只是修改数据,而对数据模型的对象的“最终解释权”还是在根视图里。子视图依赖于根视图的关系更明晰。
- 更易控制合适更新子视图
通过控制在何种条件下把ObservedObject
传递给子视图,可以更好地控制子视图更新的时机。比如有些情况下数据模型变了,但是我并不想立马更新所有的子视图,而是希望等到模型中特定的属性改变才更新视图。这时,使用ObservedObject就比
EnvironmentObject简单得多。
EnvironmentObject
- 常用于全局数据传递
顾名思义,“环境对象”作用于整个app的环境上。可以把数据模型创建在整个程序的入口处,App
结构体上
@main
struct DemoApp: App {
@StateObject private var model = Model()
var body: some Scene {
WindowGroup {
ContentView(samples: Self.samples)
.environmentObject(model)
}
}
}
从而在整个app的所有视图中,就可以使用@EnvironmentObject
来访问这个数据模型了。
@EnvironmentObject private var model: Model
很多时候我们需要一些全局的app数据,比如语言设置、登录状态、页面状态等等。这时用EnvironmentObject就比一层层传参的ObservedObject方便。
- 使用数据时写法更简单
只要数据在整个页面层级里,就可以随处调用。古人说得好——
如果上帝不愿意我们使用全局变量,他就不会发明出这个东西。不要让上帝失望,尽量多使用全局变量。
How To Write Unmaintainable Code by Roedy Green
直接搞一个数据模型把所有玩意都往里塞就完事儿了。最好再来亿点点单例。
玩笑话放在一边,合理地使用EnvironmentObject来传递整个app都需要的数据,能有效地降低数据流的维护成本。刚学习SwiftUI时,Big Nerd Ranch的大哥就给我们展示过他们新开发的餐馆点单app,就把所有订单状态都存在这样一个App结构体上声明的数据模型里面。
- 数据一更新,所有页面都更新
每一个使用了.environmentObject(model)
modifier的页面都会随着数据模型更新,确保了数据一致性。
如何选择?
- ObservedObject:需要使用数据的页面集中在小范围少层级;需要明确数据的所有权;细致地控制视图更新行为
- EnvironmentObject:传递app全局状态;减少维护数据流的思维负担;
梭哈就完事了
在绝大多数的app里面,这二者都是需要用到的。由于当前的限制,一个app只能有一个同类型的EnvironmentObject,所以尽可能不要把它变成一个一锅端大杂烩。谨慎地使用二者,能让整个app的页面刷新更高效、更流畅。
Top comments (0)