DEV Community

Ting
Ting

Posted on • Edited on

ObservedObject 对比EnvironmentObject 的优缺点

前言

在SwiftUI当中,常见的传递数据模型的方式有@ObservedObject@EnvironmentObject两种。有时候难以决断该用哪个?这里我凭自己的经验简单列举一些二者的异同,帮你抉择。

太长不看:需要传数据View的层级超过两层用EnvironmentObject,只有一层就用ObservedObject。

ObservedObject

  1. 常用于局部数据传递

比如在一个多层级的设置页面里,可以在设置的根页面创建一个

@StateObject private var model = Model()

    // … 在子视图的构造函数里
    SettingsChildView(model: model)
Enter fullscreen mode Exit fullscreen mode

然后在设置的子视图SettingsChildView里面通过@ObservedObject来传参。

@ObservedObject var model: Model
Enter fullscreen mode Exit fullscreen mode

因为传参的范围很小,所以无需超过整个设置页面的层级,也不会影响到其他的视图。

  1. 数据的所有权更加清晰

通过将数据模型声明在根视图上,整个数据传递的流向更清晰:所有的子视图只是修改数据,而对数据模型的对象的“最终解释权”还是在根视图里。子视图依赖于根视图的关系更明晰。

  1. 更易控制合适更新子视图

通过控制在何种条件下把ObservedObject传递给子视图,可以更好地控制子视图更新的时机。比如有些情况下数据模型变了,但是我并不想立马更新所有的子视图,而是希望等到模型中特定的属性改变才更新视图。这时,使用ObservedObject就比
EnvironmentObject简单得多。

EnvironmentObject

  1. 常用于全局数据传递

顾名思义,“环境对象”作用于整个app的环境上。可以把数据模型创建在整个程序的入口处,App结构体上

@main
struct DemoApp: App {
    @StateObject private var model = Model()

    var body: some Scene {
        WindowGroup {
            ContentView(samples: Self.samples)
                .environmentObject(model)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

从而在整个app的所有视图中,就可以使用@EnvironmentObject来访问这个数据模型了。

@EnvironmentObject private var model: Model
Enter fullscreen mode Exit fullscreen mode

很多时候我们需要一些全局的app数据,比如语言设置、登录状态、页面状态等等。这时用EnvironmentObject就比一层层传参的ObservedObject方便。

  1. 使用数据时写法更简单

只要数据在整个页面层级里,就可以随处调用。古人说得好——

如果上帝不愿意我们使用全局变量,他就不会发明出这个东西。不要让上帝失望,尽量多使用全局变量
How To Write Unmaintainable Code by Roedy Green

直接搞一个数据模型把所有玩意都往里塞就完事儿了。最好再来亿点点单例。

玩笑话放在一边,合理地使用EnvironmentObject来传递整个app都需要的数据,能有效地降低数据流的维护成本。刚学习SwiftUI时,Big Nerd Ranch的大哥就给我们展示过他们新开发的餐馆点单app,就把所有订单状态都存在这样一个App结构体上声明的数据模型里面。

  1. 数据一更新,所有页面都更新

每一个使用了.environmentObject(model) modifier的页面都会随着数据模型更新,确保了数据一致性。

如何选择?

  • ObservedObject:需要使用数据的页面集中在小范围少层级;需要明确数据的所有权;细致地控制视图更新行为
  • EnvironmentObject:传递app全局状态;减少维护数据流的思维负担;梭哈就完事了

在绝大多数的app里面,这二者都是需要用到的。由于当前的限制,一个app只能有一个同类型的EnvironmentObject,所以尽可能不要把它变成一个一锅端大杂烩。谨慎地使用二者,能让整个app的页面刷新更高效、更流畅。

Top comments (0)