sergey_s
sergey_s

Reputation: 143

How to get rid of waiting for Completion Handler

I load content when the user clicks on a button, wait for the download and show the content to the user. I use URLSession.shared.dataTask and return the response as (completion: @escaping closure (_ data: Data?) -> Void)

But, if the user wants to leave the controller before the content is loaded, then with self.navigationController?.popViewController (animated: true) this controller remains in memory (deinit{} don`t call). As I understand it, this is because the controller is expecting a Completion Handler callback.

What and how should I use so that when user close this view controller, he will be deleted from memory, and not stay there forever (until the user kills the application)?

Upvotes: 2

Views: 251

Answers (1)

Rob
Rob

Reputation: 437592

You said:

As I understand it, this is because the controller is expecting a Completion Handler callback.

If the view controller is not getting deallocated, it is because there is a lingering strong reference to it. In your case it sounds like your closure is keeping that strong reference. To avoid keeping that strong reference, you would use a [weak self] capture list in your closure, e.g.

let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in   // note `[weak self]` capture list
    guard let self = self else { return }    // safely unwrap the optional

    ...                                      // the rest of this like before
}
task.resume()

If you wanted to take this a step further, you would also cancel the task when you leave, e.g.

private weak var savedTask: URLSessionTask?

deinit {
    savedTask?.cancel()                          // cancel if if it hasn't finished already
}

func foo() {
    ...

    let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in   // note `[weak self]` capture list
        guard let self = self else { return }    // safely unwrap the optional
    
        ...                                      // the rest of this like before
    }
    task.resume()

    savedTask = task
}

This way, if the sole purpose of the network request is to update the UI, we can free up the network resources when the requested data is no longer required.


All of this assumes that this is the only lingering strong reference you have (i.e., that you are seeing deinit called when the network request finishes). If not, make sure that all asynchronous closures are using weak references and that you do not have any strong reference cycles. Common sources of problems would be repeating timers, observers, etc., or traditional strong reference cycles. (You can use the “debug memory graph” feature to figure out where these strong references are, such as outlined in this answer.)

Upvotes: 2

Related Questions