Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftUI memory leak when referencing property from closure inside Form/NavigationView and swapping views

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

like image 616
NeverwinterMoon Avatar asked Dec 12 '19 11:12

NeverwinterMoon


1 Answers

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.

like image 59
Oleksandr Vaker Avatar answered Nov 20 '22 06:11

Oleksandr Vaker