SwiftUI NavigationLink 内存泄漏

SwiftUI NavigationLink memory leak

我对内存管理在 SwiftUI 的 NavigationView 堆栈中的工作方式有疑问。我有一个视图,其中我已经声明了 NavigationView 和 NavigationLink,NavigationLink 的目标参数内部是我的 TestView。导航效果很好,但是当我从堆栈弹出视图时(例如向上后退按钮)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

我遇到了同样的问题,花了很多时间才弄明白。终于我明白了!使用 .navigationViewStyle(StackNavigationViewStyle())。添加是作为 NavigationView 的函数:

NavigationView {
   ...
}
.navigationViewStyle(StackNavigationViewStyle())

因为我在看 Data Essentials in SwiftUI I think, I found the answer to my question. It's new StateObject property wrapper (I could not find the documentation, but here is post 描述它)。 现在,当我希望我的数据只存在于视图范围内时,我可以使用@StateObject,而无需进行任何修改。

发生这种情况是因为默认导航样式是 ColumnNavigationViewStyle,只要水平尺寸 class 正常,它就会显示导航列表和 selected 详细信息项。

要在 运行 iPhone Pro Max 上查看此应用程序的实际效果。然后select纵向一个项目,返回,然后旋转设备。答对了。您会在第二列中看到 selected 项。

事实证明,为了让这个奇迹发生,ColumnNavigationViewStyle 将记住 selected 项,直到 selected 新项。

这反过来又给了你神秘的“保留周期”。这不是泄漏,而是它的工作原理。 (即使在像 iPhone mini 这样永远不允许第二列出现的设备上也是如此。)

其他地方提到的 .navigationViewStyle(StackNavigationViewStyle()) 修复更改了此行为。