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

 

posted @ 2017-06-09 21:19  鳄鱼不怕牙医不怕  阅读(566)  评论(0编辑  收藏  举报