Swift:OperationQueue - 在开始下一个之前等待异步调用完成(一次最多运行 2 个 URLRequests)

标签 swift nsurlrequest

tl;dr 我有一个 OperationQueue,我想同时运行两个操作。这些操作异步下载一些东西,因此它们都会立即被触发,而不是一个接一个地运行。

我通过对每张图片执行以下操作来填充一张非常大的图片表:

public func imageFromUrl(_ urlString: String) {
    if let url = NSURL(string: urlString) {
        let request = NSURLRequest(url: url as URL)
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)

        let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
            if let imageData = data as Data? {
                DispatchQueue.main.async {
                    self.setImageData(imageData)
                }
            }
        });

        task.resume()
    }

imageView.imageFromUrl(...) 一样调用它。

在较慢的互联网连接上,调用会叠加并立即开始加载每个图像。然后,用户必须等待下载的内容相互“打架”,并在所有图像同时(或多或少)出现之前盯着空白屏幕一段时间。如果一个图像接一个图像出现,对用户来说将是一个更好的体验。

我考虑过对项目进行排队,下载列表的第一个,将其从列表中删除并像这样递归调用该函数:

func doImageQueue(){

    let queueItem = imageQueue[0]

    if let url = NSURL(string: (queueItem.url)) {
        print("if let url")
        let request = NSURLRequest(url: url as URL)
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)

        let task = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
            print("in")
            if let imageData = data as Data? {
                print("if let data")
                DispatchQueue.main.async {
                    queueItem.imageView.setImageData(imageData)
                    self.imageQueue.remove(at: 0)
                    if(self.imageQueue.count>0) {
                        self.doImageQueue()
                    }
                }
            }
        });

        task.resume()
    }
}

这确实会一个接一个地加载图像,我认为一次至少运行 2 个请求是浪费时间。让我当前的实现同时处理 2 个图像会导致大的意大利面条代码,所以我研究了 Swift 的 OperationQueue

我愿意

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2

for (all images) {
    queue.addOperation {
        imageView.imageFromUrl(imageURL
    }
}

但这也会一次触发所有调用,可能是由于请求异步运行并且方法调用在等待图像下载之前结束。我该如何处理?该应用程序还将在 watchOS 上运行,也许已经有一个库,但我认为如果没有库,这应该不会太难实现。缓存不是问题。

最佳答案

如果您对 imageFromUrl 做一个小的更改,您的原始代码和操作队列和您的原始 iamgeFromUrl 方法就是您所需要的。您需要添加几行代码以确保 imageFromUrl 在下载完成之前不会返回。

这可以使用信号量来完成。

public func imageFromUrl(_ urlString: String) {
    if let url = URL(string: urlString) {
        let request = URLRequest(url: url)
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)

        let semaphore = DispatchSemaphore(value: 0)
        let task = session.dataTask(with: request, completionHandler: {(data, response, error) in
            semaphore.signal()
            if let imageData = data as Data? {
                DispatchQueue.main.async {
                    self.setImageData(imageData)
                }
            }
        });

        task.resume()
        semaphore.wait()
    }
}

如现在所写,imageFromUrl 只会在下载完成后返回。这现在允许操作队列正确运行 2 个所需的并发操作。

另请注意,代码已修改以避免使用 NSURLNSURLRequest

关于Swift:OperationQueue - 在开始下一个之前等待异步调用完成(一次最多运行 2 个 URLRequests),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49708802/

相关文章:

swift - 从基于表单的 UITableView 获取值文本字段

iOS : Do not cut socket when entring in suspended background

objective-c - NSURLRequest,为什么 craigslist 返回 404?

iphone - NSURLConnection 在 UITableView 滚动之前不是 "firing"

iphone - 如何从 Web View 中捕获自定义 URL 方案?

swift - 将 SKS 文件链接到 SpriteKit 中的自定义类

iOS 模拟器崩溃 requestAlwaysAuthorization()

ios - swift 减去两种不同的时间格式

ios - 使用 Swift 3.0 调用 Google Cloud Natural Language API

arrays - 使用 Swift 的 POST 请求中的数组