@ObservedObject 模型生命周期?

@ObservedObject model lifecycle?

我正在测试 @ObservedObject 以测试 SwiftUI 如何处理模型的生命周期。

根据我现在的理解,从 WWDC 视频和文档中,创建模型对象的视图(class 采用 ObservableObject)应该使用 @StateObject

从 WWDC 视频中,他们清楚地指出 @ObservedObject 拥有观察到的实例生命周期。

所以我创建了这个简单的设置:

struct TestParentView: View {
    
    @ObservedObject var model:TestModel
    
    var body: some View {
        
        VStack {
            Text("TEST TEXT. \(model.name)")
            TestChildView(model: model)
        }
    }
    
}

struct TestParentView_Previews: PreviewProvider {
    static var previews: some View {
        TestParentView(model: TestModel())
    }
}



struct TestChildView: View {
    
    
    @ObservedObject var model:TestModel
    
    var body: some View {
        
        Button(action: {
            print("change name")
            model.changeName()
        }, label: {
            Text("Change Name")
        })

    }
    
}

struct TestChildView_Previews: PreviewProvider {
    static var previews: some View {
        TestChildView(model: TestModel())
    }
}

我这里使用的模型是这样的:

class TestModel:ObservableObject {
    
    @Published var name:String = ""
    let id = UUID()
    
    
    func changeName() {
        name = "\(UUID())"
    }
    
    deinit {
        print("TestModel DEINIT. \(id)")
    }
    
}

当我 运行 这个应用程序时,我期望在 TestParentView 中创建的 TestModel 实例至少在某个时候被取消初始化,因为 @ObservedObject 不拥有model.

当我点击按钮触发名称更改时,一切正常,但从未调用 TestModel DEINIT。

从所有这些来看,TestParentView 似乎有一个 strong 对 TestModel 的引用,并且它永远不会放弃它。

那么,在这种情况下,当他们说 @ObservedObject 不管理 model 的生命周期时,他们是什么意思?

如果 @ObservedObject 不管理模型的生命周期,为什么从未调用 TestModel DEINIT?

我显然在这里遗漏了一些东西。

假定此视图:

struct Foo: View {
    @ObservedObject var model = TestModel()

    var body: some View {
        Text(model.name)
    }
}

每次创建此视图时,都会实例化 TestModel 的新实例。

SwiftUI 视图实际上更像是视图描述,它们在您的应用程序生命周期中被大量创建和销毁。因此,重要的是结构是轻量级的。 Foo 视图不是很轻量级,因为每次渲染过程都会实例化一个新模型。

如果您改为使用 @StateObject,则框架只会在第一次实例化 TestModel。之后它将重用同一个实例。这使它的性能更高。

经验法则:

  • 如果视图创建自己的模型(例如 Foo 视图),请使用 @StateObject
  • 如果模型是从外部传入的,则使用@ObservedObject

回答您的问题:“为什么从未调用 TestModel DEINIT”。

您声明:“当我 运行 这个应用程序时,我期望在 TestParentView 中创建的 TestModel 实例至少在某个时候取消初始化”。

微妙的细节是 TestModel 不是TestParentView 中创建的,它只是传入的。

它是在 TestParentView_Previews 中创建的。由于 TestParentView_Previews 的主体仅执行一次,因此 TestModel 也将仅初始化一次,因此永远不会被释放。