簡體   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