I have something like this:
struct SomeView: View {
@ObservedObject var viewModel: SomeViewModel
var body: some View {
NavigationView { // <- culprit
Button(action: { self.viewModel.logOut() }) { Text("X").frame(width: 40, height: 40) }
}
}
class SomeViewModel: ObservableObject {
func logOut() {
// changes global state, based on which the views are swapped, so `SomeView` is removed and replaced by a different one
}
}
When the button is pressed, SomeView
is closed and a different view is presented. But if I check the memory graph, SomeViewModel
is still allocated because self.viewModel.logOut()
is called in the Button's action closure and Button is holding the reference to SomeViewModel
.
Is there some way around this?
EDIT:
Actually, when not not wrapping the button in NavigationView
, there is no leak. As soon as I wrap the button, the leak appears. Wrapping in VStack
is working out fine. But wrapping in Form
produces the leak again. Seems like the same problem here: SwiftUI - Possible Memory Leak
I found a solution: Make a weak viewModel
in your action. It seems that Apple changed the behavior of closures. This means that the NavigationView
is storing a strong reference to viewModel. After a couple days of debugging, it finally worked for me.
Button(action: {
[weak viewModel] in viewModel?.dismissButtonPressed.send(())
}) {
Image("crossmark")
.padding()
.foregroundColor(Color.white)
}
}
In your problem, this will be solved like this:
NavigationView {
[weak viewModel] in Button(action: { viewModel?.logOut() }) {
Text("X").frame(width: 40, height: 40)
}
}
Tested on the latest Xcode 11.5, with iOS 13.5. Now, after dismissing the view, the viewModel
is correctly deallocated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With