如何使用OperationQueue处理图像下载器的URLSession
我正在尝试在 swift 中创建一个简单的 ImageDownloader
框架。
我想要实现的目标:
- 能够下载具有给定 URL 的图像
- 使用 url 字符串进行缓存
因此,仅获取一张图像是没有问题的,我只是使用了 func dataTask(with url: URL,completionHandler: @escaping (Data?, URLResponse?,错误?)->无效)-> URLSessionDataTask 从
URLSession
获取数据并将其传递到 UIImage
。
但是,我的问题是,如何将其改造成支持同时并发下载多张图片的框架呢?
我是否应该使用 OperationQueue
并且每次使用 url 创建任务时,将该任务添加到队列中?这个有必要吗??例如:
let oq = OperationQueue()
let urlArray = ["url1", "url2" ....]
for url in urlArray {
oq.addOperation {
self?.fetchImage(with: url, placeHolder: nil) { [weak self] result in
switch result {
//...
}
}
}
谢谢!
I'm trying to create a simple ImageDownloader
framework in swift.
What I'd like to achieve:
- Able to download images with given URLs
- Cache with url string
So fetching just one image is no problem, I just used func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
from URLSession
to get the data and pass it into UIImage
.
However, my question is, how should I change it into a framework that supports concurrent download many images at the same time?
Should I used OperationQueue
and every time the task is created with an url, add that task into the queue? Is this necessary?? e.g.:
let oq = OperationQueue()
let urlArray = ["url1", "url2" ....]
for url in urlArray {
oq.addOperation {
self?.fetchImage(with: url, placeHolder: nil) { [weak self] result in
switch result {
//...
}
}
}
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
不,它使用闭包返回结果,因为它已经为您执行了此操作,您不会阻塞调用,您将触发下载,可能使用线程,然后当其完成时,它将调用结果闭包,因此,您需要注意,当您获得结果时,您的应用程序状态可能已发生变化。
No, it uses a closure to return a result because it already does this for you, you are not going to block on the call, you will trigger downloading, probable using a thread, and then when its finished it will call the result closure, because of this you need to be aware that your app state could have changed when you get your result.
正如您现在可能已经发现的那样,您想要使用操作的理由是基于一些错误的假设。如果您只想支持并发网络请求,那么您根本不需要操作。这是
URLSession
的固有特征,即所有网络请求都是异步的。实际上,您必须扭曲自己,不让网络请求同时运行(我在这个论坛上看到的大多数这样做的尝试都是不明智的;菜鸟似乎喜欢信号量;哈哈)。话虽如此,您可能有一些原因想要使用操作队列,例如:
如果您想要并发,但想要控制并发程度(例如,一次不超过四个并发请求) .
如果您想要操作之间的依赖关系。
您想要取消队列上的所有操作。
但是,正确使用异步任务(例如网络请求)的操作队列需要小心。您的问题中概述的尝试是创建一个用于启动网络请求的操作,但它不会等待响应。操作立即完成。这违背了操作队列的目的。
您可能需要创建一个
Operation
子类来手动管理与各种isExecuting
、isFinished
等属性关联的 KVO。请参阅操作
文档。或者参阅尝试理解异步操作子类,了解有关如何创建异步操作子类的一般讨论异步Operation
子类。或者参见 WWDC 2015 Advanced NSOperations (它使用 Objective-C,但讨论了与异步操作相关的概念;但是这个视频已经过时了,他们实际上提取了他们的代码示例,因为他们的(恕我直言)过度设计的解决方案存在问题)。有关带有网络请求的操作队列的实际示例,请参阅 如何在 Swift 中使用 NSURLSession downloadTask 顺序下载多个文件 例如下载任务。相同的模式也可用于数据任务。综上所述,使用操作在这一点上有点不合时宜。如果支持 iOS 13 及更高版本,我使用 async-await 和 Swift 并发。甚至组合提供了比操作队列更好的管理异步任务依赖关系的模式。做你想做的事,但是,恕我直言,现在学习正确的异步操作实现的复杂性可能不是一个好的投资,因为有更好的新模式。
As you have likely figured out by now, your rationale for wanting to use operations is based upon some faulty assumptions. You simply do not need operations if all you want is to support concurrent network requests. That is an inherent feature of
URLSession
, namely that all network requests are asynchronous. You actually have to contort yourself to not have network requests run concurrently (and most attempts to do so that I've seen on this forum are ill-advised; noobs seem to love semaphores; lol).That having been said, there might be reasons why you would want to use operation queue, e.g.:
If you wanted concurrency, but you wanted to control the degree of concurrency (e.g., not more than four concurrent requests at a time).
If you wanted dependencies between operations.
You want to cancel all the operations on a queue.
But the proper use of operation queues for an asynchronous task, such as a network request, requires some care. Your attempt outlined in your question is creating an operation for the initiation of a network request, but it does not wait for the response. The operation is finishing immediately. That defeats the purpose of operation queues.
You would want to create an
Operation
subclass that manually managed the KVO associated with the variousisExecuting
,isFinished
, etc., properties. See theOperation
documentation. Or see Trying to Understand Asynchronous Operation Subclass for a general discussion about how to create asynchronousOperation
subclasses. Or see WWDC 2015 Advanced NSOperations (which uses Objective-C, but the concepts associated with asynchronous operations is discussed; but this video is outdated and they actually pulled their code samples because they had issues in their (IMHO) over-engineered solution). For a practical example of operation queues with network requests, see How To Download Multiple Files Sequentially using NSURLSession downloadTask in Swift for example with download tasks. The same pattern can be used for data tasks, too.All of that having been said, using operations is a bit of an anachronism at this point. If supporting iOS 13 and later, I use async-await and Swift concurrency. Even Combine offers better patterns for managing dependencies of asynchronous tasks than operation queues. Do what you want, but, IMHO, it might not be a good investment of time to learn the intricacies of proper asynchronous
Operation
implementations nowadays, when there are newer patterns that are so much better.