Просмотр исходного кода

Changed ImagePrefetcher to process images in a background queue instead of the main queue

Gordon Tucker 7 лет назад
Родитель
Сommit
779a37fbc8
1 измененных файлов с 25 добавлено и 10 удалено
  1. 25 10
      Sources/ImagePrefetcher.swift

+ 25 - 10
Sources/ImagePrefetcher.swift

@@ -53,6 +53,10 @@ public class ImagePrefetcher {
     /// The maximum concurrent downloads to use when prefetching images. Default is 5.
     public var maxConcurrentDownloads = 5
     
+    /// The dispatch queue to use for handling resource process so downloading does not occur on the main thread
+    /// This prevents stuttering when preloading images in a collection view or table view
+    private var prefetchQueue: DispatchQueue
+    
     private let prefetchResources: [Resource]
     private let optionsInfo: KingfisherOptionsInfo
     private var progressBlock: PrefetcherProgressBlock?
@@ -123,9 +127,17 @@ public class ImagePrefetcher {
         prefetchResources = resources
         pendingResources = ArraySlice(resources)
         
-        // We want all callbacks from main queue, so we ignore the call back queue in options
-        let optionsInfoWithoutQueue = options?.removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil))
-        self.optionsInfo = optionsInfoWithoutQueue ?? KingfisherEmptyOptionsInfo
+        // Set up the dispatch queue that all our work should occur on.
+        let prefetchQueueName = "com.onevcat.Kingfisher.PrefetchQueue"
+        prefetchQueue = DispatchQueue(label: prefetchQueueName)
+        
+        // We want all callbacks from our prefetch queue, so we should ignore the call back queue in options
+        var optionsInfoWithoutQueue = options?.removeAllMatchesIgnoringAssociatedValue(.callbackDispatchQueue(nil)) ?? KingfisherEmptyOptionsInfo
+        
+        // Add our own callback dispatch queue to make sure all callbacks are coming back in our expected queue
+        optionsInfoWithoutQueue.append(.callbackDispatchQueue(prefetchQueue))
+        
+        self.optionsInfo = optionsInfoWithoutQueue
         
         let cache = self.optionsInfo.targetCache
         let downloader = self.optionsInfo.downloader
@@ -142,8 +154,8 @@ public class ImagePrefetcher {
      */
     public func start()
     {
-        // Since we want to handle the resources cancellation in main thread only.
-        DispatchQueue.main.safeAsync {
+        // Since we want to handle the resources cancellation in the prefetch queue only.
+        prefetchQueue.async {
             
             guard !self.stopped else {
                 assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.")
@@ -176,7 +188,7 @@ public class ImagePrefetcher {
      Stop current downloading progress, and cancel any future prefetching activity that might be occuring.
      */
     public func stop() {
-        DispatchQueue.main.safeAsync {
+        prefetchQueue.async {
             if self.finished { return }
             self.stopped = true
             self.tasks.values.forEach { $0.cancel() }
@@ -244,7 +256,7 @@ public class ImagePrefetcher {
     }
     
     func reportCompletionOrStartNext() {
-        DispatchQueue.main.async {
+        prefetchQueue.async {
             if let resource = self.pendingResources.popFirst() {
                 self.startPrefetching(resource)
             } else {
@@ -255,8 +267,11 @@ public class ImagePrefetcher {
     }
     
     func handleComplete() {
-        completionHandler?(skippedResources, failedResources, completedResources)
-        completionHandler = nil
-        progressBlock = nil
+        // The completion handler should be called on the main thread
+        DispatchQueue.main.safeAsync {
+            self.completionHandler?(self.skippedResources, self.failedResources, self.completedResources)
+            self.completionHandler = nil
+            self.progressBlock = nil
+        }
     }
 }