SDWebImagePrefetcherDelegate
1 @protocol SDWebImagePrefetcherDelegate2 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 prefetched10 * @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 prefetched12 */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 prefetcher18 * @param totalCount The total number of images that were prefetched (whether successful or not)19 * @param skippedCount The total number of images that were skipped20 */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) iddelegate;
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 list5 *6 * @param urls list of URLs to prefetch7 */8 - (void)prefetchURLs:(nullable NSArray*)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 prefetched10 * @param completionBlock block to be called when prefetching is completed11 * first param is the number of completed (successful or not) requests,12 * second parameter is the number of skipped requests13 */14 - (void)prefetchURLs:(nullable NSArray*)urls15 progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock16 completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock;
指定 URLs 列表给 SDWebImagePrefetcher 的预取队列。目前一次下载一个图像,跳过下载失败的图像,然后继续进入列表的下一次下载。
progressBlock 是下载进度更新时调用的 block。这个 block 的第一个参数是已经完成的或者不需要请求的数量。第二个参数是最开始需要预取的图像数量。
completionBlock 是预取完成时调用的 block。这个 block 的第一个参数是已经完成的或者不需要请求的数量。第二个参数是跳过的请求的数量,请求失败的数量。
1 /**2 * Remove and cancel queued list3 */4 - (void)cancelPrefetching;
移除并取消队列列表。
SDWebImagePrefetcher.m
1 @interface SDWebImagePrefetcher () 2 3 @property (strong, nonatomic, nonnull) SDWebImageManager *manager; 4 @property (strong, nonatomic, nullable) NSArray*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*)urls { 2 [self prefetchURLs:urls progress:nil completed:nil]; 3 } 4 5 - (void)prefetchURLs:(nullable NSArray *)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 concurrency20 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 failed18 self.skippedCount++;19 }20 if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) {21 [self.delegate imagePrefetcher:self22 didPrefetchURL:self.prefetchURLs[index]23 finishedCount:self.finishedCount24 totalCount:self.prefetchURLs.count25 ];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:self5 didFinishWithTotalCount:(total - self.skippedCount)6 skippedCount:self.skippedCount7 ];8 }9 }
执行全部下载完毕的代理方法。
参考链接:
END