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

问题描述 投票:0回答:2

我正在尝试了解 Swift 语言中的内存泄漏情况,但有一种情况我仍然想知道。

我创建了一个新的 UIViewController 并调用 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()
时,为什么在这种情况下内存没有泄漏?

swift memory-management urlsession retain-cycle strong-reference-cycle
2个回答
1
投票

未完成的任务将永远不会执行其完成处理程序,因为它永远不会完成。因此,任务及其处理程序将保留在内存中。

我们不知道 

resume

的内部实现,但框架在执行完成处理程序后丢弃它们似乎是明智的。这将打破保留周期并允许视图控制器被释放。

您可以通过在完成处理程序和 deinit 方法中添加额外的日志记录来确认这一点 - 我希望视图控制器在完成处理程序运行之前不会被释放。


0
投票

这行代码

URLSession*

强烈捕获
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) } }

添加到 ViewController 的

self.vcTask = nil
方法中以手动打破循环。
    

© www.soinside.com 2019 - 2024. All rights reserved.