SwiftUI-NavigationView中的内存泄漏 [英] SwiftUI - memory leak in NavigationView

查看:201
本文介绍了SwiftUI-NavigationView中的内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试向模式显示的View的导航栏添加一个关闭按钮.但是,关闭后,再也不会调用我的视图模型 deinit 方法.我发现问题在于它捕获了 navigationBarItem 中的自身.我不能只在 navigationBarItem 的操作中传递weak self,因为View是一个结构而不是一个类.这是一个有效的问题还是只是缺乏知识?

I am trying to add a close button to the modally presented View's navigation bar. However, after dismiss, my view models deinit method is never called. I've found that the problem is where it captures the self in navigationBarItem's. I can't just pass a weak self in navigationBarItem's action, because View is a struct, not a class. Is this a valid issue or just a lack of knowledge?

struct ModalView: View {

    @Environment(\.presentationMode) private var presentation: Binding<PresentationMode>
    @ObservedObject var viewModel: ViewModel

    var body: some View {

        NavigationView {
            Text("Modal is presented")
            .navigationBarItems(leading:
                Button(action: {
                    // works after commenting this line
                    self.presentation.wrappedValue.dismiss()
                }) {
                    Text("close")
                }

            )
        }
    }
}

推荐答案

您无需在自己的视图中拆分关闭按钮.您可以通过将捕获列表添加到NavigationView的闭包:这将破坏保留您的viewModel的参考周期.

You don't need to split the close button out in its own view. You can solve this memory leak by adding a capture list to the NavigationView's closure: this will break the reference cycle that retains your viewModel.

您可以在操场上复制/粘贴此示例代码,以查看它已解决了问题(Xcode 11.4.1,iOS操场).

You can copy/paste this sample code in a playground to see that it solves the issue (Xcode 11.4.1, iOS playground).

import SwiftUI
import PlaygroundSupport

struct ModalView: View {
    @Environment(\.presentationMode) private var presentation
    @ObservedObject var viewModel: ViewModel

    var body: some View {
        // Capturing only the `presentation` property to avoid retaining `self`, since `self` would also retain `viewModel`.
        // Without this capture list (`->` means `retains`):
        // self -> body -> NavigationView -> Button -> action -> self
        // this is a retain cycle, and since `self` also retains `viewModel`, it's never deallocated.
        NavigationView { [presentation] in
            Text("Modal is presented")
                .navigationBarItems(leading: Button(
                    action: {
                        // Using `presentation` without `self`
                        presentation.wrappedValue.dismiss()
                },
                    label: { Text("close") }))
        }
    }
}

class ViewModel: ObservableObject { // << tested view model
    init() {
        print(">> inited")
    }

    deinit {
        print("[x] destroyed")
    }
}

struct TestNavigationMemoryLeak: View {
    @State private var showModal = false
    var body: some View {
        Button("Show") { self.showModal.toggle() }
            .sheet(isPresented: $showModal) { ModalView(viewModel: ViewModel()) }
    }
}

PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.setLiveView(TestNavigationMemoryLeak())

这篇关于SwiftUI-NavigationView中的内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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