繁体   English   中英

Swift:OperationQueue-等待异步调用完成,然后再开始下一次(一次最多运行2个URLRequest)

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

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(...)那样调用它。

在较慢的Internet连接上,呼叫会堆叠起来,并立即开始加载每个图像。 然后,用户必须等待下载内容相互“打架”,并在空白图像全部一次(或多或少)出现之前凝视空白屏幕。 如果一个图像接一个出现,对于用户来说将是一个更好的体验。

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

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进行少量更改, imageFromUrl需要带有操作队列的原始代码和iamgeFromUrl方法imageFromUrl 您需要添加几行代码,以确保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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM