我正在测试@ObservedObject,以测试SwiftUI如何处理模型的生命周期。
我现在从WWDC视频和文档中了解到,创建模型对象(采用ObservableObject
的类(的View应该使用@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
似乎有一个对TestModel的strong引用,并且它从未放弃它。
那么,当他们说@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
要回答您的问题:";为什么测试模型DEINIT从未被称为">
您声明:";当我运行这个应用程序时,我希望在TestParentView中创建的TestModel实例至少在某个时候被取消初始化;。
微妙的细节是,TestModel是而不是在TestParentView
中创建的,它只是传入的。
它是在TestParentView_Previews
中创建的。由于TestParentView_Previews
的主体只执行一次,所以TestModel也只初始化一次,因此永远不会被释放。