SwiftUI中@State变量的生命周期是什么?

4

如果我创建一个新的@State变量,它什么时候会被销毁?它的生命周期是在父UIHostingController中吗?

据我所知,这并没有被记录下来。这很重要,因为如果我在视图层次结构中的某个位置上将ObservableObject作为State创建,我不知道如何清理它。

import SwiftUI

struct Example: View {
    @State private var foo = Foo()
    var body: some View {
        Text("My Great View")
    }
}

class Foo: ObservableObject {
    deinit {
        // When will this happen?
        print("Goodbye!")
    }
}

我对stateSwiftUI不是很熟悉,但它不仅基于Foo的类型,即如果它是引用类型,则每当其保留计数降至零时。如果它是变量类型,则您实际上不需要关心它们的生命周期。 - mfaani
问题是关于在 SwiftUI 中变量何时(如果有的话)会被 deinit 的。 - Wil Gieseler
ObservableObject作为State至少是一个误解,它必须与ObservedObject配对使用。此外...属性包装器State(和ObservedObject)是结构体,值类型。 - Asperi
2个回答

3
假设:

假设:

struct Example: View {
    @State private var foo = Foo()
    var body: some View {
        Text("My Great View")
    }
}

class Foo: ObservableObject {
    init() {
        print(#function)
    }
    deinit {
        print(#function)
    }
}

问题在于,View类型是一个struct,它的body不是一组在实时执行的函数集合,而是在渲染Viewbody时同时初始化的。

问题场景:

struct ContentView: View {
    @State var isPresented = false
    var body: some View {
        NavigationView {
            NavigationLink(destination: Example()) {
                Text("Test")
            }
        }
    }
}

如果你注意到了,Example.init在导航发生之前就被调用了,而在弹出时Example.deinit根本没有被调用。原因是当ContentView初始化时,它必须也初始化其body中的所有内容。所以Example.init将会被调用。
当我们导航到Example时,它已经被初始化了,因此Example.init不会再次被调用。当我们从Example弹出时,我们只是回到了ContentView,但由于Example可能再次需要使用,并且它不是实时创建的,所以它不会被销毁。
只有当ContentView完全被删除时,Example.deinit才会被调用。
我不确定这一点,但在另一篇文章中找到了一个类似的问题: 为了证明这一点,让我们确保ContentView被完全删除。
以下示例利用操作表单来呈现和从视图层次结构中删除它。

工作场景:

struct ContentView: View {
    @State var isPresented = false
    var body: some View {
        Button(action: { self.isPresented.toggle() }) {
            Text("Test")
        }
        .sheet(isPresented: $isPresented) {
            Example()
                .onTapGesture {
                    self.isPresented.toggle()
            }
        }
    }
}

PS:即使未声明为@State的类也适用,与ObservableObject没有真正的关联。


所以当视图消失时,变量也会消失。但在我的测试中并非如此,我的示例中的deinit从未被调用。 - Wil Gieseler
@WilGieseler 好的,会在playground测试中查看。 - staticVoidMan
@WilGieseler 我成功调用了 deinit 但是我必须确保 View 被移除。已经更新了答案。 - staticVoidMan
@WilGieseler 抱歉,那是我的复制粘贴错误。我正在研究带有和不带有 @State 的类生命周期,当 View 完全被移除时,两者都会调用 deinit。但是当 View 仍然是层次结构的一部分时,不会调用 deinit - staticVoidMan
顺便问一下,@WilGieseler,你到底想弄清楚什么?即使不使用@State@ObservableObject,情况也是一样的。因此,问题的根源可能在于SwiftUI内部机制的其他方面。 - staticVoidMan
显示剩余3条评论

-2
在iOS 14中,正确的方法是使用@StateObject。在@State中存储引用类型没有安全的方式。

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