Home > Software engineering >  ObservedObject is still in memory after the view is dismissed, Memory Leak?
ObservedObject is still in memory after the view is dismissed, Memory Leak?

Time:01-23

I'm making an app with SwiftUI and UIkit, I use UIkit for the main app controller and navigation, and I use SwiftUI for app design.

The app works very well, but I'm worried about the memory leaks. This is because the ViewModels I use to pass data between views don't call desinit whene the view disappears. I know that in SwiftUI views are not disposed immediately, but since I'm using UIKit to navigate I don't know what the problem is.

//The ViewModel for each user fetched
internal class UserViewModel: ObservableObject, Identifiable {
    
    //MARK: - Propeties var currentListener: ListenerRegistration?
    
    @Published var request: Request?
    @Published var user: User
    
    init(user: User) {
        self.user = user
        getRequest()
        fetchAdmins()
    }
    
    deinit {
        //Dosnt get called removeListener()
    }
    
    func getRequest() {
        guard let uid = Auth.auth().currentUser?.uid else {return}
        guard let id = id else {return}
        
        self.currentListener = Collections.requests(id).document(uid).addSnapshotListener { snapshot, error in
            
            if let error = error {
                print(error.localizedDescription)
                return
            }
            
            if ((snapshot?.exists) != nil) {
                if let request = try? snapshot!.data(as: Request.self) {
                    DispatchQueue.main.async {
                        self.request = request
                    }
                }
            }
        }
    }
    
    func removeListener() {
        self.currentListener?.remove()
    }
}
}

//The ViewModel to fetch all the users ViewModels
class UsersViewModel: ObservableObject {
    @Published var users = [UserViewModel]()
    
    func fetch() {
        DispatchQueue.global(qos: .background).async {
            Collections.users.getDocuments(completion: { snapshot, err in
                
                guard let documents = snapshot?.documents else { return } let users = documents.compactMap({ try? $0.data(as: User.self) })
                
                users.forEach { user in
                    let vm = UserViewModel(user: user)
                    DispatchQueue.main.asyncAfter(deadline: .now()   1) {
                        self.users.append(vm)
                    }
                }
            })
        }
    } }

//Show the users cells with the ViewModel
struct HomeView: View {
    @ObservedObject var usersViewModels: UsersViewModel
    
    //MARK: - Init
    init() {
        self.usersViewModels = UsersViewModel()
    }
    
    var body: some View {
        ListView(content: {
            ForEach(usersViewModels) { usersViewModel in
                UserCell(viewModel: usersViewModel).id(user.id)
            }
        })
    }
}

This is how I navigate between controllers and views of my app. I don't use NavigationLinks:

    public static func push<Content: View>(view: Content) {
        DispatchQueue.main.async {
 guard let tabBarController = UIApplication.rootViewController as? UITabBarController, let navigationController = tabBarController.selectedViewController as? UINavigationController else { return nil }

            if let navigationController = UIApplication.getCurrentNavigationController() {
                navigationController.pushViewController(HostingController(content: view), animated: true)
            }
        }
    }

Does anyone know if this method that I am using to navigate can cause me memory problems? And you know why my app doesn't reduce its memory every time I close a window, it just increases more and more.

Memory Leak Xcode Tool

CodePudding user response:

The disappearing does not mean it is no longer in memory.

It looks like you keep pushing them onto the navigation stack which increases their retain count.

CodePudding user response:

You've got a memory leak here:

struct HomeView: View {
    @ObservedObject var usersViewModels: UsersViewModel
    
    //MARK: - Init
    init() {
        self.usersViewModels = UsersViewModel() // here
    }

View structs must not init objects because the View struct is recreated every state change thus the object is being constantly init.

SwiftUI is all about taking advantage of value semantics, try to use @State with value types (or group them in a struct) in the View struct for your view data.

Model data structs go in a singleton ObservableObject supplied to the Views using .environmentObject.

  • Related