视图层次结构中的 SwiftUI 保留周期

SwiftUI retain cycle in view hierarchy

我有以下具有保留周期的视图层次结构,这是我可以重现问题的最简单方法。所有视图模型和属性都必须保留,因为它们在原始解决方案中是必需的:

import SwiftUI

struct MainView: View {
    @StateObject var viewModel = MainViewModel()

    var body: some View {
        NavigationView { [weak viewModel] in
            VStack {
                Button("StartCooking") {
                    viewModel?.show()
                }

                if viewModel?.isShowingContainerView == true {
                    ContainerView()
                }
                Button("StopCooking") {
                    viewModel?.hide()
                }
            }
        }
        .navigationViewStyle(.stack)
    }
}

final class MainViewModel: ObservableObject {
    @Published var isShowingContainerView = false

    func show() {
        isShowingContainerView = true
    }

    func hide() {
        isShowingContainerView = false
    }
}

struct ContainerView: View {
    @Namespace var namespace

    var body: some View {
        VStack {
            SubView(
                namespace: namespace
            )
        }
    }
}


struct SubView: View {
    @StateObject var viewModel = SubViewModel()
    var namespace: Namespace.ID

    var body: some View {
        Text("5 min")
            .matchedGeometryEffect(id: UUID().uuidString, in: namespace)
            .onTapGesture {
                foo()
            }
    }

    private func foo() {}
}


final class SubViewModel: ObservableObject {}

如果我 运行 应用程序,点击 StartCooking,而不是 StopCooking 并检查内存图,我仍然看到 SubViewModel 的实例,这意味着此代码中存在泄漏。 如果我删除:

保留循环已解决。不幸的是我需要所有这些。您能看出问题可能是什么吗?如何解决?

看起来像是一个 SwiftUI 错误。一种可能的解决方法(如果 sub-view 是一个或有限的集合)是使用视图模型工厂来提供实例。

这是一个视图的示例:

struct SubView: View {

    @StateObject var viewModel = SubViewModel.shared  // single instance !!

    // .. other code
}

final class SubViewModel: ObservableObject {
    static var shared = SubViewModel()         // << this !!
}

我可以通过在 SubViewModel 和 运行 中使每个 属性 可选成为 SubView 消失时的函数来解决它,这使得它们 nilSubViewModel 仍然留在内存中,但不会占用那么多 space。

有趣的是,我什至尝试将视图模型设为可选,并在视图消失时将其设为零,但它仍然留在内存中。