2016-06-17 46 views
1

我試圖在加載圖像時更新進度條。我在這裏閱讀了幾個答案,並試圖用不同的方式對我的代碼進行格式化。我正在嘗試閱讀圖像並更新進度欄。然後,將加載所有圖像調用代碼來處理它們。我得到的最好結果是大部分時間都有效的代碼。但是,如果我正在處理一個拉大量圖像的情況,我會發現奇怪的錯誤。我認爲它會在所有圖像完全加載之前繼續運行繼續代碼。當我刪除dispatch_async時,代碼工作正常,但進度條不更新。使用dispatch_async在後臺加載圖像

func imageLocXML(il:String) { 
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { 
     let url:NSURL? = NSURL(string: il) 
     let data:NSData? = NSData(contentsOfURL: url!) 
     let image = UIImage(data: data!) 
     pieceImages.append(image!) 
     self.numImagesLoaded += 1 
     self.updateProgressBar() 
     if self.numImagesLoaded == self.numImagesToLoad { 
      self.continueLoad() 
     } 
    } 
} 
+1

歡迎來到SO,請在您的問題中投票您的答案,謝謝。 –

回答

4

有一些問題:

  1. 此代碼是不是線程安全的,因爲你numImagesLoaded有競爭狀態。理論上,這可能導致continueLoad被多次調用。您可以通過同步numImagesLoaded來實現線程安全,方法是將更新(和其他模型對象)分派回主隊列。

  2. 就像DashAndRest說的那樣,你也必須將UI更新派發到主隊列中。

  3. 當您進行此異步操作時,您在啓動大量請求時引入了網絡超時風險。您可以通過重構代碼來解決此問題,以使用操作隊列而不是調度隊列,並指定maxConcurrentOperationCount

  4. 的圖像被添加到陣列中:

    • 由於這些任務異步運行,他們不能保證完成任何特定的順序,並且因此該陣列將不會在順序。您應該將圖像保存在字典中,在這種情況下,訂單不再重要。

    • 就像numImagesLoaded一樣,pieceImages不是線程安全的。

  5. 你正在使用很多強制解包,所以如果有任何請求失敗,這將崩潰。

但是爲了解決這個問題,我們必須退後一步,看看調用這個方法的例程。讓我們想象一下,你有這樣的:

var pieceImages = [UIImage()] 

func loadAllImages() { 
    for imageUrl in imageURLs { 
     imageLocXML(imageUrl) 
    } 
} 

func imageLocXML(il:String) { 
    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) { 
     let url:NSURL? = NSURL(string: il) 
     let data:NSData? = NSData(contentsOfURL: url!) 
     let image = UIImage(data: data!) 
     self.pieceImages.append(image!) 
     self.numImagesLoaded += 1 
     self.updateProgressBar() 
     if self.numImagesLoaded == self.numImagesToLoad { 
      self.continueLoad() 
     } 
    } 
} 

我建議你更換的東西,如:

var pieceImages = [String: UIImage]() 

func loadAllImages() { 
    let queue = NSOperationQueue() 
    queue.maxConcurrentOperationCount = 4 

    let completionOperation = NSBlockOperation { 
     self.continueLoad() 
    } 

    for imageURL in imageURLs { 
     let operation = NSBlockOperation() { 
      if let url = NSURL(string: imageURL), let data = NSData(contentsOfURL: url), let image = UIImage(data: data) { 
       NSOperationQueue.mainQueue().addOperationWithBlock { 
        self.numImagesLoaded += 1 
        self.pieceImages[imageURL] = image 
        self.updateProgressBar() 
       } 
      } 
     } 

     queue.addOperation(operation) 
     completionOperation.addDependency(operation) 
    } 

    NSOperationQueue.mainQueue().addOperation(completionOperation) 
} 

話雖如此,我認爲還有更深層次的問題在這裏:

  • 如果你被加載提前相似圖片呢?我們通常會建議延遲加載圖像,只在需要時加載它們。

  • 如果你打算將圖像加載到這樣的結構,你應該妥善處理內存壓力,清除它在內存不足警告。然後,您需要妥善處理,當你去獲取的圖像和它的被清除,由於內存壓力(導致你的右後衛到剛剛在時間延遲加載模式)做什麼。

  • 我們通常不建議對同步網絡請求(該​​)。我們一般會使用NSURLSession這是取消的,更豐富的提供錯誤處理,等等。誠然,上面的代碼進一步複雜化(可能導致我失望異步NSOperation子之路),但你至少應該知道的侷限性contentsOfURL

0

嘗試DISPATCH_QUEUE_PRIORITY_DEFAULT此背景隊列爲:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { 
    let url:NSURL? = NSURL(string: il) 
    let data:NSData? = NSData(contentsOfURL: url!) 
    let image = UIImage(data: data!) 
    self.pieceImages.append(image!) 
    self.numImagesLoaded += 1 
    dispatch_async(dispatch_get_main_queue(), { 
     //UI must be updated on main thread queue 
     self.updateProgressBar() 
    }) 
    if self.numImagesLoaded == self.numImagesToLoad { 
     self.continueLoad() 
    } 
} 

UI必須在主線程隊列更新!

您還必須使用self訪問pieceImages

相關問題