在Swift的属性中存储URLSession任务时出现内存泄漏情况

r1wp621o  于 2023-01-25  发布在  Swift
关注(0)|答案(2)|浏览(130)

我试图理解Swift语言中的内存泄漏情况,但有一种情况我仍然想知道。
我创建了一个新的UIViewController并调用fetch函数,将fetch任务存储在一个属性中,但没有启动任务,然后关闭了此UIViewController。
我发现此UIViewController中的***deinit函数未被调用***(内存泄漏)。

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
}

但如果我调用resume方法调用fetch函数,然后再次关闭UIViewController。
我发现这个UIViewController中的***deinit函数被称为***(内存不泄漏)。

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
}

现在我认为如果我在UIViewController的属性中存储一个任务,并且在回调中使用self,它将创建一个导致内存泄漏的循环。
但是当我调用task.resume()时为什么在这种情况下内存没有泄漏?

y1aodyip

y1aodyip1#

一个un-resume d任务将永远不会执行它的完成处理程序,因为它永远不会完成。因此,任务及其处理程序将保留在内存中。
我们不知道URLSession*的内部实现,但框架在完成处理程序执行后丢弃它们似乎是明智的,这将打破保留循环,并允许视图控制器被释放。
您可以通过在完成处理程序和deinit方法中添加额外的日志记录来确认这一点-我希望视图控制器在完成处理程序运行之前不会被释放。

ca1c2owp

ca1c2owp2#

(加上@jrturton的答案,答案100%正确)
这行代码

let task = URLSession.shared.downloadTask(with: url) { _, _, _ in ... }

强烈捕获self,导致内存泄漏。
避免这种情况的一种方法是将捕获更改为weak,如下所示:

let task = URLSession.shared.downloadTask(with: url) { [weak self] _, _, _ in
    guard let self else { return }
    DispatchQueue.main.async {
        print(self.view.description)
    }
}

或者,尝试将self.vcTask = nil添加到ViewController的viewDidDisappear方法以手动打破循环。

相关问题