gpt4 book ai didi

ios - 如何解决 Swift 4 URLSession.downloadTask 中的内存循环问题?

转载 作者:搜寻专家 更新时间:2023-10-31 08:17:15 24 4
gpt4 key购买 nike

我有一个基本的 viewController,它带有一个按钮,当点击该按钮时,它会调用一个方法开始从给定的有效 url 下载图像。

我非常小心地将任何强指针传递给 View Controller 。而且我能够关闭 View Controller 并返回到呈现 Controller 而不会出现任何问题。然而,由 View Controller 创建的 Web() 的对象实例不会被释放,即使 View Controller 的 deinit 被调用。

我做错了什么?

顺便说一句:文件下载开始,报告进度和文件位置。但是,一旦文件下载完成,该对象就永远不会被释放。

viewController.swift

@IBAction func buttonTapped(_ sender: UIButton) {
//first create an instance of Web class (download helper)
let web = Web(url: "https://www.google.com.sa/images/branding/googlelogo/2x/googlelogo_color_120x44dp.png")

// all call back closures have no reference to self
web.downloadFile(
finishedHandler: {fileLocation in print("file stored to \(fileLocation)")},
progressHandler: {bytes,total in print("written \(bytes) out of \(total)")},
errorHandler: {msg in print("error: \(msg)")}
)

}

并且我将 Web() 定义为 NSObject,它符合 URLSessionDownloadDelegate 以利用委托(delegate)事件方法 (didFinishDownload/didWriteBytes 等)。

网络.swift

import UIKit

class Web : NSObject {
var urlToDownload : String?

// the following variables are references to closures passed to the object.
var progressCallback : ((_ bytesWritten:Int64, _ totalExpectedBytes: Int64)->())?
var finishedCallback : ((_ fileLocation: String)->())?


static var instanceCount = 0 // keep track of number of instances created

init(url: String) {

Web.instanceCount += 1
urlToDownload = url
print(" new instance of Web created. Total : \(Web.instanceCount)")
}

deinit {
Web.instanceCount -= 1
print("Web instance deallocated. Remaining: \(Web.instanceCount)")

}
}

然后我将文件下载逻辑添加为同一文件的扩展:

extension Web : URLSessionDownloadDelegate {

func downloadFile(
finishedHandler: @escaping (_ fileLocation:String)->(),
progressHandler: @escaping (_ bytesWritten:Int64, _ totalBytes: Int64)->(),
errorHandler: @escaping (_ errorMsg:String)->()) {

// we need to capture the closure because, these will
// be called once the delegate methods are triggered
self.progressCallback = progressHandler
self.finishedCallback = finishedHandler


if let url = URL(string: self.urlToDownload!) {
let session = URLSession(
configuration: .default,
delegate: self,
delegateQueue: nil)

let task = session.downloadTask(with: url)

task.resume()

}

}

// MARK :- Delegate methods
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
// call the closure if it still exists
self.finishedCallback?(location.absoluteString)
}

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
// report progress by calling captured closure, if exists
self.progressCallback?(totalBytesWritten,totalBytesExpectedToWrite)
}
}

最佳答案

在我跟踪问题之后(使用Memory Graph Debugger),我弄清楚了这段代码(Web 类 - 下载文件方法)中的问题原因:

if let url = URL(string: self.urlToDownload!) {
let session = URLSession(
configuration: .default,
delegate: self,
delegateQueue: nil)

let task = session.downloadTask(with: url)

task.resume()
}

实际上有一个与实例化URLSession相关的问题没有摆脱它;因此,你必须做一种 - 手动处理来解决它,调用 finishTasksAndInvalidate()适合这样的问题:

if let url = URL(string: self.urlToDownload!) {
let session = URLSession(
configuration: .default,
delegate: self,
delegateQueue: nil)

let task = session.downloadTask(with: url)

task.resume()
// here we go:
session.finishTasksAndInvalidate()
}

为了确保它按预期工作,我建议在 Web 类中的委托(delegate)方法和 deinit 添加断点,您应该会看到它按预期工作(委托(delegate)方法可以工作并且然后将调用 deinit


此外:

如果您想了解更多有关如何使用内存图调试器的信息,您可以查看:How to debug memory leaks when Leaks instrument does not show them?

关于ios - 如何解决 Swift 4 URLSession.downloadTask 中的内存循环问题?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49770673/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com