SDWebImage源码阅读(十一)SDWebImagePrefetcher
SDWebImagePrefetcherDelegate
1 @protocol SDWebImagePrefetcherDelegate <NSObject> 2 3 @optional 4 5 /** 6 * Called when an image was prefetched. 7 * 8 * @param imagePrefetcher The current image prefetcher 9 * @param imageURL The image url that was prefetched 10 * @param finishedCount The total number of images that were prefetched (successful or not) 11 * @param totalCount The total number of images that were to be prefetched 12 */ 13 - (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(nullable NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount; 14 15 /** 16 * Called when all images are prefetched. 17 * @param imagePrefetcher The current image prefetcher 18 * @param totalCount The total number of images that were prefetched (whether successful or not) 19 * @param skippedCount The total number of images that were skipped 20 */ 21 - (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount; 22 23 @end
代理提供了两个方法:
1.每次下载完成一个图片,finishedCount 表示对图像进行预取的总数(成功或失败)totalCount 图像将被预取的总数。
2.当所有的图像下载完毕,totalCount 对图像进行预取的总数(无论成功或者失败) skippedCount 跳过的图像的总数,表示下载失败的的总数。
命名block
1 typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls); 2 typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls);
SDWebImagePrefetcher 属性
1 /** 2 * The web image manager 3 */ 4 @property (strong, nonatomic, readonly, nonnull) SDWebImageManager *manager; 5 6 /** 7 * Maximum number of URLs to prefetch at the same time. Defaults to 3. 8 */ 9 @property (nonatomic, assign) NSUInteger maxConcurrentDownloads; 10 11 /** 12 * SDWebImageOptions for prefetcher. Defaults to SDWebImageLowPriority. 13 */ 14 @property (nonatomic, assign) SDWebImageOptions options; 15 16 /** 17 * Queue options for Prefetcher. Defaults to Main Queue. 18 */ 19 @property (nonatomic, assign, nonnull) dispatch_queue_t prefetcherQueue; 20 21 @property (weak, nonatomic, nullable) id <SDWebImagePrefetcherDelegate> delegate;
SDWebImageManager *manager 网络图像管理器
NSUInteger maxConcurrentDownloads 在同一时间预取的 URL 的最大数目,默认是 3 。
SDWebImageOptions options 预取的选项,默认是 SDWebImageLowPriority。
dispatch_queue_t prefetcherQueue 预取的队列,默认是主线程。
id<SDWebImagePrefetcherDelegate> delegate 代理棒。
SDWebImagePrefetcher 方法
1 /** 2 * Return the global image prefetcher instance. 3 */ 4 + (nonnull instancetype)sharedImagePrefetcher;
返回一个全局的预取实例。单例对象。
1 /** 2 * Allows you to instantiate a prefetcher with any arbitrary image manager. 3 */ 4 - (nonnull instancetype)initWithImageManager:(nonnull SDWebImageManager *)manager NS_DESIGNATED_INITIALIZER;
实例化一个预取图像管理对象。
1 /** 2 * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, 3 * currently one image is downloaded at a time, 4 * and skips images for failed downloads and proceed to the next image in the list 5 * 6 * @param urls list of URLs to prefetch 7 */ 8 - (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls;
指定 URLs 列表给 SDWebImagePrefetcher 的预取队列。目前一次下载一个图像,跳过下载失败的图像,然后继续进入列表的下一次下载。
1 /** 2 * Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching, 3 * currently one image is downloaded at a time, 4 * and skips images for failed downloads and proceed to the next image in the list 5 * 6 * @param urls list of URLs to prefetch 7 * @param progressBlock block to be called when progress updates; 8 * first parameter is the number of completed (successful or not) requests, 9 * second parameter is the total number of images originally requested to be prefetched 10 * @param completionBlock block to be called when prefetching is completed 11 * first param is the number of completed (successful or not) requests, 12 * second parameter is the number of skipped requests 13 */ 14 - (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls 15 progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock 16 completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock;
指定 URLs 列表给 SDWebImagePrefetcher 的预取队列。目前一次下载一个图像,跳过下载失败的图像,然后继续进入列表的下一次下载。
progressBlock 是下载进度更新时调用的 block。这个 block 的第一个参数是已经完成的或者不需要请求的数量。第二个参数是最开始需要预取的图像数量。
completionBlock 是预取完成时调用的 block。这个 block 的第一个参数是已经完成的或者不需要请求的数量。第二个参数是跳过的请求的数量,请求失败的数量。
1 /** 2 * Remove and cancel queued list 3 */ 4 - (void)cancelPrefetching;
移除并取消队列列表。
SDWebImagePrefetcher.m
1 @interface SDWebImagePrefetcher () 2 3 @property (strong, nonatomic, nonnull) SDWebImageManager *manager; 4 @property (strong, nonatomic, nullable) NSArray<NSURL *> *prefetchURLs; 5 @property (assign, nonatomic) NSUInteger requestedCount; 6 @property (assign, nonatomic) NSUInteger skippedCount; 7 @property (assign, nonatomic) NSUInteger finishedCount; 8 @property (assign, nonatomic) NSTimeInterval startedTime; 9 @property (copy, nonatomic, nullable) SDWebImagePrefetcherCompletionBlock completionBlock; 10 @property (copy, nonatomic, nullable) SDWebImagePrefetcherProgressBlock progressBlock; 11 12 @end
属性。
1 + (nonnull instancetype)sharedImagePrefetcher { 2 static dispatch_once_t once; 3 static id instance; 4 dispatch_once(&once, ^{ 5 instance = [self new]; 6 }); 7 return instance; 8 }
单例方法实现。
1 - (nonnull instancetype)init { 2 return [self initWithImageManager:[SDWebImageManager new]]; 3 } 4 5 - (nonnull instancetype)initWithImageManager:(SDWebImageManager *)manager { 6 if ((self = [super init])) { 7 _manager = manager; 8 _options = SDWebImageLowPriority; 9 _prefetcherQueue = dispatch_get_main_queue(); 10 self.maxConcurrentDownloads = 3; 11 } 12 return self; 13 }
初始化方法。
1 - (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads { 2 self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads; 3 } 4 5 - (NSUInteger)maxConcurrentDownloads { 6 return self.manager.imageDownloader.maxConcurrentDownloads; 7 }
maxConcurrentDownloads 的 setter 和 getter 方法。其实是给 self.manager.imageDownloader.maxConcurrentDownloads 赋值和取值。
1 - (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls { 2 [self prefetchURLs:urls progress:nil completed:nil]; 3 } 4 5 - (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls 6 progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock 7 completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock { 8 [self cancelPrefetching]; // Prevent duplicate prefetch request 9 self.startedTime = CFAbsoluteTimeGetCurrent(); 10 self.prefetchURLs = urls; 11 self.completionBlock = completionBlock; 12 self.progressBlock = progressBlock; 13 14 if (urls.count == 0) { 15 if (completionBlock) { 16 completionBlock(0,0); 17 } 18 } else { 19 // Starts prefetching from the very first image on the list with the max allowed concurrency 20 NSUInteger listCount = self.prefetchURLs.count; 21 for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) { 22 [self startPrefetchingAtIndex:i]; 23 } 24 } 25 }
指定 NSURL 的数组去下载。在开始前先调用了 cancelPrefetching 方法,防止重复预取下载。
1 - (void)cancelPrefetching { 2 self.prefetchURLs = nil; 3 self.skippedCount = 0; 4 self.requestedCount = 0; 5 self.finishedCount = 0; 6 [self.manager cancelAll]; 7 }
调用该方法后,所有的未完成的下载都会被清空,也就说现在 SDWebImagePrefetcher 只专注处理传进来的 NSURL 的数组,是无状态的下载,也就是要求传入的 NSURL 要完整。然后循环去调用下载方法。
1 - (void)startPrefetchingAtIndex:(NSUInteger)index { 2 if (index >= self.prefetchURLs.count) return; 3 self.requestedCount++; 4 [self.manager loadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) { 5 if (!finished) return; 6 self.finishedCount++; 7 8 if (image) { 9 if (self.progressBlock) { 10 self.progressBlock(self.finishedCount,(self.prefetchURLs).count); 11 } 12 } 13 else { 14 if (self.progressBlock) { 15 self.progressBlock(self.finishedCount,(self.prefetchURLs).count); 16 } 17 // Add last failed 18 self.skippedCount++; 19 } 20 if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) { 21 [self.delegate imagePrefetcher:self 22 didPrefetchURL:self.prefetchURLs[index] 23 finishedCount:self.finishedCount 24 totalCount:self.prefetchURLs.count 25 ]; 26 } 27 if (self.prefetchURLs.count > self.requestedCount) { 28 dispatch_async(self.prefetcherQueue, ^{ 29 [self startPrefetchingAtIndex:self.requestedCount]; 30 }); 31 } else if (self.finishedCount == self.requestedCount) { 32 [self reportStatus]; 33 if (self.completionBlock) { 34 self.completionBlock(self.finishedCount, self.skippedCount); 35 self.completionBlock = nil; 36 } 37 self.progressBlock = nil; 38 } 39 }]; 40 }
该方法按 index 下标开始并行下载图片。 self.requestedCount、self.finishedCount、self.skippedCount 分别在对应的地方做 ++ 操作。执行一张图片下载完成后的代理方法,如果 self.requestedCount 小于 self.prefetchURLs.count 则异步在 self.prefetcherQueue 队列里面继续下载。
当全部下载完毕后,执行全部下载完毕的代理方法,执行下载完成的 block。
1 - (void)reportStatus { 2 NSUInteger total = (self.prefetchURLs).count; 3 if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) { 4 [self.delegate imagePrefetcher:self 5 didFinishWithTotalCount:(total - self.skippedCount) 6 skippedCount:self.skippedCount 7 ]; 8 } 9 }
执行全部下载完毕的代理方法。
参考链接:http://www.jianshu.com/p/9b629dbd4d30
END