@EnvironmentObject和@ObservedObject有什么区别?

27

我一直在阅读有关SwiftUI中属性包装器的内容,我发现它们非常棒,但是有一件事情我真的没有搞懂,那就是@EnvironmentObject@ObservedObject之间的区别。

根据我目前所学,我知道@EnvironmentObject用于在我们的应用程序中需要使用对象的多个位置,但我们不需要将其通过所有位置传递。例如,如果我们有层次结构A -> B -> C -> D,并且该对象在A中创建,则它保存在环境中,以便我们可以将其直接从A传递到D(如果D需要它)。

如果我们使用 @ObservedObject ,该对象在A中创建并且需要传递给D,则我们还需要经过B和C。

但我仍然不知道如何决定使用哪一个。

这里有两个我做的示例项目:

struct ContentView2: View {
 
   var order = Order2()

   var body: some View {
      VStack {
           EditView2()
           DisplayView2()
       }
       .environmentObject(order)
   }
}
struct EditView2: View {
   @EnvironmentObject var user: Order2
 
   var body: some View {
       HStack{
       TextField("Fruit", text: $user.item)
       }
   }
}
struct DisplayView2: View {
   @EnvironmentObject var user: Order2
   var body: some View {
       VStack{
       Text(user.item)
       }
   }
}
class Order2: ObservableObject {
       @Published var item = "Orange"
   }

struct ContentView: View {

    var order = Order()
    
    var body: some View {
       VStack {
            EditView(order: order)
            DisplayView(order: order)
        }
    }
}
struct EditView: View {
    @ObservedObject var order: Order
    var body: some View {
        HStack{
        TextField("Fruit", text: $order.item)
        }
    }
}
struct DisplayView: View {
      @ObservedObject var order: Order
      var body: some View {
        VStack{
        Text(order.item)
        }
    }
}
class Order: ObservableObject {
    @Published var item = "Apple"
}

这两个代码都执行相同的视图更新。此外,这两个ContentView都传递了一个Order对象。区别在于Environment通过.environmentObject(order)进行传递,而Observed则直接传递EditView(order: order)。对我来说,它们都完成了相同的工作,只是声明方式不同,因此我希望能得到一些解释或更好的示例。


4
区别仅在于注入... ObservedObject 您必须像任何其他属性一样在每个初始化时进行注入,但是 environment object 仅在视图层次结构的根部注入一次... 并且可用于任何深度的视图。就是这样。 - Asperi
1个回答

27

正如您所注意到的,需要从视图传递@ObservedObject。当您没有太多视图时,对于简单的视图层次结构可能会更好。


假设您有以下层次结构:

ViewA -> ViewB -> ViewC -> ViewD

如果你想让ViewA中的@ObservedObject也在ViewB中,那么直接在init中传递是没有问题的。

但是,如果你还想在ViewD中使用它呢?如果你不需要在ViewBViewC中使用它呢?

对于@ObservedObject,你需要手动从ViewA传递到ViewB,然后再传递到ViewC,最后传递到ViewD。而且你需要在每个子视图中声明它。

对于@EnvironmentObject,很容易 - 只需将它传递给顶层视图:

ViewA().environmentObject(someObservableObject)

那么您只需在使用它的视图中声明它-这可能会使您的代码更易读。


注意

每个环境中的对象(视图层次结构)都可以访问注入的@EnvironmentObject。如果您不希望这样(隐私很重要),则可能需要将其作为@ObservedObject传递。


1
非常感谢您提供的详细说明。总结一下,当我在ViewA中声明@EnvironmentObject时,所有子视图都将可以访问它。那么对于sheet视图也是这样吗?例如,我从ViewA中呈现一个.sheet。 - Dakata
3
@Dakata 不好意思,你需要再次将其注入到你的表格中。但是只需要在调用时注入一次即可。然后表格中的所有子视图都可以访问你的EnvironmentObject。可以将其视为表格拥有自己的环境。 - pawello2222
2
请注意,您可以将私有类型用作EnvironmentObject,这可以防止不知道该类型的范围访问它。 - Casper Zandbergen
2
这是最好的解释,我完全理解了。谢谢! - Binaya Thapa Magar
1
进一步说明之前的评论,EnvironmentObject 也可与 sheet 一起使用。 - Kramer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接