I'm trying to understand the memory leak situation in Swift language but there is a situation that I'm still wondering.
I've created a new UIViewController and call fetch function with storing the fetch task in a property without starting the task then I closed this UIViewController.
I found that the deinit function in this UIViewController is not called (Memory leak).
func fetchAPI() {
let url = URL(string: "https://www.google.com")!
let task = URLSession.shared.downloadTask(with: url) { _, _, _ in
DispatchQueue.main.async {
print(self.view.description)
}
}
self.vcTask = task
}
But If I call the fetch function with calling resume
method and then I close UIViewController again.
I found that the deinit function in this UIViewController is called (Memory not leak).
func fetchAPI() {
let url = URL(string: "https://www.google.com")!
let task = URLSession.shared.downloadTask(with: url) { _, _, _ in
DispatchQueue.main.async {
print(self.view.description)
}
}
self.vcTask = task
task.resume() // start downloading
}
For now I think that if I store a task in a property in UIViewController and I use self
in the callback. It would create a cycle that caused Memory leak.
But when I call task.resume()
Why the memory is not leak in this situation?
An un-
resume
d task will never execute its completion handler, because it will never complete. The task, and its handler, will therefore remain in memory.We don't know the internal implementation of
URLSession*
but it would seem sensible for the framework to discard completion handlers once they are executed. This would break the retain cycle and allow the view controller to be deallocated.You could confirm this by adding extra logging in the completion handler and deinit method - I would expect the view controller not to be deallocated until the completion handler has run.