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

Swift: OperationQueue - wait for asynchronous calls to finish before starting next (have maximum of 2 URLRequests running at a time)

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

我通过对每个图像执行以下操作来填充 table 个非常大的图像:

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(...) 一样称呼它。

在较慢的互联网连接上,调用会叠加并立即开始加载每张图片。然后,用户必须等待下载到 "fight" 彼此,并且在所有图像一次(或多或少)出现之前盯着空白屏幕一段时间。如果一个图像一个接一个地出现,对用户来说会是一个更好的体验。

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

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 个请求 运行ning 是浪费时间。让我当前的实现同时处理 2 个图像会导致大的意大利面条代码,所以我研究了 Swift 的 OperationQueue

我愿意

let queue = OperationQueue()
queue.maxConcurrentOperationCount = 2

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

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

如果您对 imageFromUrl 做一点小改动,您只需要包含操作队列的原始代码和原始 iamgeFromUrl 方法。您需要添加几行代码以确保 imageFromUrl 在下载完成之前不会 return。

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

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 下载完成后只会 return。这现在允许操作队列正确地 运行 2 个所需的并发操作。

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