SwiftUI @ObservedObject viewmodel 在 List 的详细视图中从未发布 [英] SwiftUI @ObservedObject viewmodel in detail-view of List never released

查看:30
本文介绍了SwiftUI @ObservedObject viewmodel 在 List 的详细视图中从未发布的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含多个项目的列表,它们打开一个 DetailView,而后者又包含一个视图模型.视图模型应该有一个服务类,该服务类在详细视图出现时进行初始化,并在返回时取消初始化.

I have a List with several items, that open a DetailView which in turn holds a viewmodel. The viewmodel is supposed to have a service class that gets initialized when the detail view appears and should be deinitialized when navigating back.

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

However, the first problem is that in my example below, all 3 ViewModel instances are created at the same time (when ContentView is displayed) and never get released from memory (deinit is never called).

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 的工作方式,但我很难想出一种方法来处理属于详细视图状态的一部分但不应该在详细视图实际出现之前实例化的类对象.一个例子是带有房间的视频会议应用程序,其中建立连接等的房间客户端应该只在实际进入房间时初始化,并在离开房间时取消初始化.

This is probably just how SwiftUI works, but I have a hard time thinking of a way to handle class objects that are part of a detail view's state, but are not supposed to instantiate until the detail view actually appears. An example would be video conferencing app, with rooms, where the room client that establishes connections etc. should only get initialized when actually entering the room and deinitialize when leaving the room.

如果您有任何有关如何管理此问题的建议,我将不胜感激.我应该在 onAppear 或类似的地方初始化 heavyClient 吗?

I'd appreciate any suggestions on how to mange this. should I initialize the heavyClient at onAppear or something similar?

推荐答案

问题是 DetailView() 被初始化为导航链接的一部分.一种可能的解决方案是这篇文章中的LazyView.

The problem is that DetailView() is getting initialized as part of the navigation link. One possible solution could be the LazyView from this post.

实现如下:

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

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

And then wrap the DetailView() in the 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 实例存在,尽管这是一个很大的改进.

The only issue with this workaround is that there seems to always be one instance of ViewModel sitting around, though it's a large improvement.

这篇关于SwiftUI @ObservedObject viewmodel 在 List 的详细视图中从未发布的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆