11

我对 SwiftUI 的 NavigationView 堆栈中的内存管理如何工作有疑问。我有一个视图,其中我声明了 NavigationView 和 NavigationLink,NavigationLink 的目标参数内部是我的 TestView。导航效果很好,但是当我从堆栈中弹出视图(fe 向上后退按钮)时,控制台中不会打印 deinit,并且仍然可以在内存图中找到 TestViewModel。当不再需要我的 TestViewModel 时,如何取消它的初始化?

    /// First view in application
    struct ContentView: View {

        var body: some View {
            NavigationView {
                VStack {
                    Text("Hello, leak!")
                    NavigationLink(
                        destination: TestView(viewModel: TestViewModel()),
                        label: { Text("Create leak ‍♂️") }
                    )
                }
            }
        }
    }

    /// Just simple class for init and deinit print
    class TestViewModel: ObservableObject {

        @Published var text = "Test"

        init() {
            print("TestViewModel init")
        }

        deinit {
            print("TestViewModel deinit")
        }
    }

    /// Second view, which is poped from stack
    private struct TestView: View {

        @ObservedObject var viewModel: TestViewModel

        var body: some View {
            Text(viewModel.text)
        }
    }

更新 添加了内存图屏幕截图,我已经忘记了。

内存图截图底部

内存图截图顶部

更新

在导航工作的真实设备上进行了测试。看起来,视图模型在弹出视图时没有取消初始化,而是在再次推送时再次初始化。但是问题仍然存在,有没有办法在导航堆栈中弹出视图时取消初始化视图模型?

TestViewModel init
TestViewModel deinit
TestViewModel init

此外,当我向堆栈添加另一个视图时,行为会发生一些变化。现在 second 的视图视图模型将导致泄漏,但 first 将按预期取消初始化。

First view push
TestViewModel init
Second view push
TestViewModel2 init
Second view pop
First view pop
TestViewModel deinit
4

2 回答 2

17

我遇到了同样的问题,花了很多时间来解决这个问题。最后我得到了它!使用.navigationViewStyle(StackNavigationViewStyle()). 添加是作为 NavigationView 的函数:

NavigationView {
   ...
}
.navigationViewStyle(StackNavigationViewStyle())
于 2020-06-22T09:19:36.487 回答
4

当我在 SwiftUI 中观看 Data Essentials 时,我想,我找到了问题的答案。它是新的 StateObject 属性包装器(我找不到文档,但这里是描述它的帖子)。现在,当我希望我的数据只存在于视图范围内时,我可以使用@StateObject,而无需进行任何黑客攻击。

于 2020-06-27T12:58:34.007 回答