Claus
Claus

Reputation: 5722

Memory leak using Firebase

I execute an API call in Firebase for retrieving the user profile information and storing it in a ViewController member variable. The API is declared as a static function inside a class MyApi:

// Get User Profile
    static func getUserProfile(byID userId:String,response:@escaping (_ result:[User]?,_ error:Error?)->()) {

        // check ID is valid
        guard userId.length > 0 else {
            print("Error retrieving Creator data: invalid user id provided")
            response(nil,ApiErrors.invalidParameters)
            return
        }

        // retrieve profile
        let profilesNode = Database.database().reference().child(MyAPI.profilesNodeKey)
        profilesNode.child(userId).observe(.value, with: { (snapshot) in
            // check if a valid data structure is returned
            guard var dictionary = snapshot.value as? [String:AnyObject] else {
                print("Get User Profile API: cannot find request")
                response([],nil)
                return
            }

            // data mapping
            dictionary["key"] = userId as AnyObject
            guard let user = User(data:dictionary) else {
                print("Get User Profile API: error mapping User profile data")
                response(nil,ApiErrors.mappingError)
                return
            }
            response([user], nil)
        }) { (error) in
            response(nil,ApiErrors.FirebaseError(description: error.localizedDescription))
        }
    }

and I call it like that:

MyAPI.getUserProfile(byID: creatorId) { (profiles, error) in
            guard let profiles = profiles, profiles.count > 0 else {
                Utility.showErrorBanner(message: "Error retrieving Creator profile")
                print("Error retrieving creator profile ID:[\(creatorId)] \(String(describing: error?.localizedDescription))")
                return
            }
            self.currentProfile = profiles.first!
        }

The ViewController is called in Modal mode so it should be deallocated every time I exit the screen.

Problem: a huge chunk of memory get allocated when I enter the screen, but it doesn't get freed up when I leave it. I'm sure about this because the problem doesn't appear if I remove the line self.currentProfile = profiles.first! (obviously)

How can I avoid this from happening?

NOTE: currentProfile is of type User, which was used to be a struct. I made it a class so I could use a weak reference for storing the information:

weak var currentCreator: User? {
        didSet {
            updateView()
        }
    }

but the problem still persists.

Upvotes: 1

Views: 1441

Answers (1)

Doug Stevenson
Doug Stevenson

Reputation: 317372

You are adding an observer:

profilesNode.child(userId).observe(...)

But you never remove it. As long as that observe is still added, it will hold on to memory from the entire set of results, and continually retrieve new updates. It's a really bad practice not to remove your observers.

If you want to read data just a single time, there is a different API for that using observeSingleEvent.

Upvotes: 4

Related Questions