List的详细视图中的SwiftUI @ObservedObject视图模型从未发布

时间:2020-07-21 01:53:42

标签: swift swiftui combine

我有一个包含多个项目的列表,这些列表打开一个DetailView,后者又包含一个视图模型。该视图模型应该具有一个服务类,该服务类将在详细视图出现时初始化,并应在向后导航时取消初始化。

但是,第一个问题是,在下面的示例中,将同时创建所有3个ViewModel实例(显示ContentView时),并且从不从内存中释放(deinit永远不会被调用。)

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                NavigationLink(destination: DetailView()) {
                    Text("Link")
                }
                NavigationLink(destination: DetailView()) {
                    Text("Link")
                }
                NavigationLink(destination: DetailView()) {
                    Text("Link")
                }
            }
        }
    }
}

struct DetailView: View {
    @ObservedObject var viewModel = ViewModel()

    var body: some View {
        Text("Hello \(viewModel.name)")
    }
}

class ViewModel: ObservableObject {

    @Published var name = "John"

    private let heavyClient = someHeavyService()

    init() { print("INIT VM") }

    deinit { print("DEINIT VM") }
}

这可能只是SwiftUI的工作方式,但是我很难思考一种处理类对象的方法,这些类对象是详细视图状态的一部分,但不应实例化,直到详细视图真正出现为止。一个示例是带有会议室的视频会议应用程序,其中建立连接的会议室客户端等应该仅在实际进入会议室时初始化,而在离开会议室时取消初始化。

对于有关如何进行管理的任何建议,我将不胜感激。我应该在heavyClient上初始化onAppear还是类似的东西?

1 个答案:

答案 0 :(得分:0)

问题在于DetailView()正在作为导航链接的一部分进行初始化。一种可能的解决方案是this post中的LazyView

实现如下:

struct LazyView<Content: View>: View {
    let build: () -> Content
    init(_ build: @autoclosure @escaping () -> Content) {
        self.build = build
    }
    var body: Content {
        build()
    }
}

然后将DetailView()包裹在LazyView()中:

struct ContentView: View {
    var body: some View {
        NavigationView {
            List {
                NavigationLink(destination: LazyView(DetailView())) {
                    Text("Link")
                }
                NavigationLink(destination: LazyView(DetailView())) {
                    Text("Link")
                }
                NavigationLink(destination: LazyView(DetailView())) {
                    Text("Link")
                }
            }
        }
    }
}

这种解决方法的唯一问题是,尽管有很大的改进,但似乎总是有一个ViewModel实例存在。