Swift 重复 URLSession 造成大量内存泄漏

Posted

技术标签:

【中文标题】Swift 重复 URLSession 造成大量内存泄漏【英文标题】:Swift Repetitive URLSession Creates Massive Memory Leaks 【发布时间】:2017-08-21 19:43:43 【问题描述】:

我有一个应用程序(部分)向用户显示 Wi-Fi 连接的当前下载速度。它通过打开一个URLSession 并下载一个中等大小 (~10MB) 的文件并测量它所花费的时间来做到这一点。

这是 URLSession 函数:

func testSpeed() 

    Globals.shared.dlStartTime = Date()
    Globals.shared.DownComplete = false

    if Globals.shared.currentSSID == "" 
        Globals.shared.bandwidth = 0
        Globals.shared.DownComplete = true
     else 

        let url = URL(string: [HIDDEN])
        let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
        let task = session.downloadTask(with: url!)

        task.resume()
    


public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) 


    Globals.shared.dlFileSize = (Double(totalBytesExpectedToWrite) * 8) / 1000
    let progress = (Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)) * 100.0

    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "ProcessUpdating"), object: nil, userInfo: ["progress" : progress])


public func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) 
    let elapsed = Double( Date().timeIntervalSince(Globals.shared.dlStartTime))
    Globals.shared.bandwidth = Int(Globals.shared.dlFileSize / elapsed)
    Globals.shared.DownComplete = true
    Globals.shared.dataUse! += (Globals.shared.dlFileSize! / 8000)
    session.invalidateAndCancel()

    NotificationCenter.default.post(name: NSNotification.Name(rawValue: "ProcessFinished"), object: nil, userInfo: nil)

正如您可能从委托函数中看出的那样,这一切都存在于与视图控制器分开的类中,以及其他一些小型网络功能,例如获取 IP 和 SSID。代表发布视图控制器观察到的通知。

我的 ViewController 有一个 NSTimer,它每 5 秒回调一次此 URLSession 以重新测试速度(但只有在前一个完成时才运行它)。这是代码:

reloadTimer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(rescanNetwork), userInfo: nil, repeats: true)

调用这个函数:

func backgroundRescan() 
    if Globals.shared.DownComplete 
        Networking().testSpeed()
    

它再次运行 URL Session,当然要检查以确保前一个已经完成。

由于某种原因,我在测试中使用了大量内存,直到应用程序达到 2GB 的内存使用并被控制台输出 Message from debugger: Terminated due to memory issue 终止。这一切都发生在运行应用程序的两分钟内。

我什至在完成委托中添加了session.invalidateAndCancel(),以拼命尝试清除该记忆。但它没有用。我错过了什么吗?

【问题讨论】:

您确定 Globals.shared.currentSSID 设置为 "" 以外的值吗?如果没有,您将每 5 秒添加一个进程,直到您炸毁为止。 您是否在某处对这些代表进行了强有力的参考?班级是什么样的? 不要每次都实例化一个新的 URLSession。这是 URLSession 的一个已知问题。 Mozahler:这不是真的。除非找到有效的 SSID,否则根本不会调用 URLSession; IE。您实际上已连接到本地网络。尝试和测试,没有问题。 Ssswift:没有强引用。 @Rob 还有其他选择吗?我可以告诉现有会话重新下载相同的文件吗? 纠正我自己,URLSession 问题出现在您重复实例化但从不使其无效的情况下。但是您正在使其无效,因此这不太可能是问题所在。此外,URLSession 泄漏问题是以 kb 为单位衡量的,因此它不太可能是 gb 内存消耗的来源。它一定是别的东西。我会在 Xcode 8 中使用“调试内存图”功能并查找您认为应该释放但没有释放的对象,它会告诉您强引用在哪里(参见 ***.com/a/30993476/1271826)。 【参考方案1】:

正如 Rob 所说,将 URLSessionConfiguration.default 作为变量放入您的类中并使用它。 我遇到了同样的问题,最后我通过使用一个包含我所有通信方法的单一类解决了它,并将 URLSessionConfiguration.default 设置为成员变量。我所有的内存泄漏都解决了。

【讨论】:

我需要更多信息。如果我将任务变量移出函数,我也必须将会话变量移出,因为任务变量使用该会话变量。这一切都很好,但是会话变量让我将delegate: self 参数更改为delegate: self as! URLSessionDelegate。当我这样做时,代表不再被调用。解决方法?

以上是关于Swift 重复 URLSession 造成大量内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

Swift UrlSession 在 UrlSession 中不起作用

Swift 3 URLSession 发送空请求

迁移到 Swift3 的问题,URLSession.GET 的编译错误

如何在 Swift 中使用 URLSession 缓存图像

将 URLSession 更新为 swift 3 抛出错误

swift--使用URLSession异步加载图片