When exactly SwiftUI releases ObservableObjects

1.5k views Asked by At

I am trying to learn how SwiftUI works internally in terms of memory management. I have little doubt about it.

When I add a NavigationLink to the 2nd View which has some Search Functionality and also loading some data from the cloud.

Now when I came back to the root view, my observableObject class is still in memory.

Does anyone have any idea how SwiftUI manages the memory and release objects?

Here is a sample code of my experiment.

struct ContentView: View {
    var body: some View {
        NavigationView {
            DemoView(screenName: "Home")
                .navigationBarHidden(true)
        }
    }
}

struct DemoView:View {
    var screenName:String
    var body: some View {
        VStack{
             
            NavigationLink(destination: SecondView(viewModel:SecondViewModel())) {
                Text("Take Me To Second View")
            }
            
            Text(self.screenName)
        } 
    }
}


// Second View
class SecondViewModel:ObservableObject {
    @Published var search:String = ""
    @Published var user:[String] = []
    
    func fetchRecords() -> Void {
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) { [weak self] in
            self?.user = ["Hello", "There"]
        }
    }
    
}

struct SecondView:View {
    @ObservedObject var viewModel:SecondViewModel
    
    var body: some View {
        VStack {
            TextField("Search Here", text: $viewModel.search)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .padding()
            List(self.viewModel.user, id:\.self) { user in
                Text("User \(user)")
            }
        }.onAppear{
            self.viewModel.fetchRecords()
        }
    }
}

And this is what I received in-memory graph.

enter image description here

2

There are 2 answers

0
Denis Krivitski On BEST ANSWER

The object lifecycle in SwiftUI is as usual. An object is deallocated by ARC when there are no more references to it. You can add deinit { print("deinit")} to your SecondViewModel and see when the object is deallocated. And yes, in your case a new SecondViewModel object will be created each time the DemoView body is evaluated, which is probably not what you want. I guggest you initialize and store the SecondViewModel object outside of the view hierarchy, and pass a reference to this global object in DemoView.body .

1
Asperi On

Ok, I probably don't remember other similar post on the same issue, but the reason of it because your SecondView, cause it is a value, still is in NavigationView when you press back, as long as until another NavigationLink is activated.

So you need either to have different independent life-cycle for SecondViewModel or, if remain as-is, to add some reset/cleanup for it, so only pure empty object left, ie

    }.onAppear{
        self.viewModel.fetchRecords()
    }.onDisappear {
        self.viewModel.cleanup()
    }