What is the difference between @StateObject and @ObservedObject in child views in swiftUI

张黒猫*_*张黒猫 3 swift swiftui combine observedobject

I created a Model like this:

class TestModel: ObservableObject {
    @Published var num: Int = 0
}
Run Code Online (Sandbox Code Playgroud)

Model is be used in "Home" view and "Home"s child view "HomeSub"

struct Home: View {
    
    @StateObject var model = TestModel()
    
    var body: some View {
        NavigationView(content: {
            NavigationLink(destination: HomeSub(model: model)) { Text("\(model.num)") }
        })
    }
}
Run Code Online (Sandbox Code Playgroud)
struct HomeSub: View {
   //1
    @StateObject var model = TestModel()
   //2
    @ObservedObject var model = TestModel()

    var body: some View {
        VStack {
            Text("\(model.num)")
                .padding()
                .background(Color.red)
            Button("Add") {
                model.num += 1
            }
        }
        .onChange(of: model.num, perform: { value in
            print("homeSub: \(value)")
        })
        
    }
}
Run Code Online (Sandbox Code Playgroud)

In HomeSub view, what is the difference between 1 and 2? When I run the project, they have exactly the same behavior.

vac*_*ama 6

正如您所写, 和 都@StateObject@ObservedObject子视图中执行相同的操作。但是,两者都不正确,因为他们不必要地创建一个新的TestModel,只是为了扔掉它并用传入的替换它。

子视图的正确写法是:

@ObservedObject var model: TestModel
Run Code Online (Sandbox Code Playgroud)

在这种情况下,不会在子视图中分配初始值model,这意味着调用者必须提供它。这正是您想要的。真理的来源之一就是model父母的观点。

此外,状态变量( 和@State@StateObject应该是private视图,并且应该始终标记为private。如果你这样做了:

@StateObject private var model = TestModel()
Run Code Online (Sandbox Code Playgroud)

在子视图中,那么您将无法从父视图传递模型,并且您会看到只能@ObservedObject在这种情况下使用。


经过进一步测试,似乎 Swift/SwiftUITestModel在编写为 时避免在子视图中创建@ObservedObject var model = TestModel(),但该语法仍然会误导读者,并且仍然应该编写为,@ObservedObject var model: TestModel因为这清楚地表明model正在从某处初始化else(即从父视图)。