使用 NavigationLink 推送视图时,已发布的 ViewModel 会导致内存泄漏 [英] Published ViewModel causes memory leak when pushing a view with NavigationLink

查看:50
本文介绍了使用 NavigationLink 推送视图时,已发布的 ViewModel 会导致内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我之前问过

顶部引用 (view.content.vm) 具有种类:(AnyViewStorage in $7fff57ab1a78)>> 和层次结构:SwiftUI.(AnyViewStorage in $7fff57ab1a78)<SwiftUI.ModifiedContent<MyApp.UserView, SwiftUI.(RelationshipModifier in $7fff57ad2760)<Swift.String>>AnyViewStorageBase _TtCs12_SwiftObject

是什么导致了这种内存泄漏,我该如何消除它?

解决方案

请参考这篇文章 (

虽然不如可选链接那么漂亮,但它可以完成工作.

I previously asked a question about how to push a view with data received from an asynchronous callback. The method I ended up with has turned out to cause a Memory Leak.

I'm trying to structure my app with MVVM for SwiftUI, so a ViewModel should publish another ViewModel, that a View then knows how to present on screen. Once the presented view is dismissed from screen, I expect the corresponding ViewModel to be deinitialised. However, that's never the case with the proposed solution.

After UserView is dismissed, I end up having an instance of UserViewModel leaked in memory. UserViewModel never prints "Deinit UserViewModel", at least not until next time a view is pushed on pushUser.

struct ParentView: View {
    @ObservedObject var vm: ParentViewModel
    
    var presentationBinding: Binding<Bool> {
        .init(get: { vm.pushUser != nil },
              set: { isPresented in
                if !isPresented {
                    vm.pushUser = nil
                }
              }
        )
    }
    
    var body: some View {
        VStack {
            Button("Get user") {
                vm.getUser()
            }
            Button("Read user") {
                print(vm.pushUser ?? "No userVm")
            }
            if let userVm = vm.pushUser {
                NavigationLink(
                    destination: UserView(vm: userVm),
                    isActive: presentationBinding,
                    label: EmptyView.init
                )
            }
        }
    }
}

class ParentViewModel: ObservableObject {
    @Published var pushUser: UserViewModel? = nil
    
    var cancellable: AnyCancellable?
    
    private func fetchUser() -> AnyPublisher<User, Never> {
        Just(User.init(id: "1", name: "wiingaard"))
            .delay(for: .seconds(1), scheduler: DispatchQueue.main)
            .eraseToAnyPublisher()
    }
    
    func getUser() {
        cancellable = api.getUser().sink { [weak self] user in
            self?.pushUser = UserViewModel(user: user)
        }
    }
}

struct User: Identifiable {
    let id: String
    let name: String
}

class UserViewModel: ObservableObject, Identifiable {
    deinit { print("Deinit UserViewModel") }

    @Published var user: User
    init(user: User) { self.user = user }
}

struct UserView: View {
    @ObservedObject var vm: UserViewModel    
    var body: some View {
        Text(vm.user.name)
    }
}

After dismissing the UserView and I inspect the Debug Memory Graph, I see an instance of UserViewModel still allocated.

The top reference (view.content.vm) has kind: (AnyViewStorage in $7fff57ab1a78)<ModifiedContent<UserView, (RelationshipModifier in $7fff57ad2760)<String>>> and hierarchy: SwiftUI.(AnyViewStorage in $7fff57ab1a78)<SwiftUI.ModifiedContent<MyApp.UserView, SwiftUI.(RelationshipModifier in $7fff57ad2760)<Swift.String>>> AnyViewStorageBase _TtCs12_SwiftObject

What's causing this memory leak, and how can I remove it?

解决方案

Please refer to this post (https://stackoverflow.com/a/62511130/11529487), it solved the issue for the memory leak bug in SwiftUI by adding on the NavigationView:

.navigationViewStyle(StackNavigationViewStyle())

However it breaks the animation, there is a hacky solution to this issue. The animation problem occurs because of the optional chaining in "if let".

When setting "nil" as the destination in the NavigationLink, it essentially does not go anywhere, even if the "presentationBinding" is true.

I invite you to try this piece of code as it fixed the animtaion problem that resulted from the StackNavigationViewStyle (and no memory leaks):

Although not as pretty as the optional chaining, it does the job.

这篇关于使用 NavigationLink 推送视图时,已发布的 ViewModel 会导致内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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