> 第十篇

## 前言
我们先看看`SDWebImage`主文件的组成模块:

![](http://images2015.cnblogs.com/blog/637318/201701/637318-20170122150927488-934404619.png)

可以看出来,每个模块即独立又相对关联,当最后拼接出`SDWebImageManager`的时候,我们就可以利用它来做一些有意思的事情。

本篇就主要讲解其中的一个使用场景:批量图片下载。记得之前有一位同学有这样的开发需求:他们公司要做一个漫画APP,漫画都是由图片组成的,每一个本漫画由很多章节组成,需要提供一个缓存功能,也就是把图片一组一组的下载下来。那么使用本篇的这个类就能完美的解决它的需求。

## SDWebImagePrefetcherDelegate
这个代理提供了两个方法来监听事件:

- 每次下载完一个图片
- 所有的都下载完

代码:

@protocol SDWebImagePrefetcherDelegate

@optional

/**
* Called when an image was prefetched.
*
* @param imagePrefetcher The current image prefetcher
* @param imageURL The image url that was prefetched
* @param finishedCount The total number of images that were prefetched (successful or not)
* @param totalCount The total number of images that were to be prefetched
*/
- (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(nullable NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount;

/**
* Called when all images are prefetched.
* @param imagePrefetcher The current image prefetcher
* @param totalCount The total number of images that were prefetched (whether successful or not)
* @param skippedCount The total number of images that were skipped
*/
- (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount;

@end
## SDWebImagePrefetcher.h
#### 属性:
/**
* The web image manager
*/
@property (strong, nonatomic, readonly, nonnull) SDWebImageManager *manager;

/**
* Maximum number of URLs to prefetch at the same time. Defaults to 3.
*/
@property (nonatomic, assign) NSUInteger maxConcurrentDownloads;

/**
* SDWebImageOptions for prefetcher. Defaults to SDWebImageLowPriority.
*/
@property (nonatomic, assign) SDWebImageOptions options;

/**
* Queue options for Prefetcher. Defaults to Main Queue.
*/
@property (nonatomic, assign, nonnull) dispatch_queue_t prefetcherQueue;

@property (weak, nonatomic, nullable) id delegate;

#### 初始化:
/**
* Return the global image prefetcher instance.
*/
+ (nonnull instancetype)sharedImagePrefetcher;

/**
* Allows you to instantiate a prefetcher with any arbitrary image manager.
*/
- (nonnull instancetype)initWithImageManager:(nonnull SDWebImageManager *)manager NS_DESIGNATED_INITIALIZER;
#### 方法:
/**
* Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching,
* currently one image is downloaded at a time,
* and skips images for failed downloads and proceed to the next image in the list
*
* @param urls list of URLs to prefetch
*/
- (void)prefetchURLs:(nullable NSArray *)urls;

/**
* Assign list of URLs to let SDWebImagePrefetcher to queue the prefetching,
* currently one image is downloaded at a time,
* and skips images for failed downloads and proceed to the next image in the list
*
* @param urls list of URLs to prefetch
* @param progressBlock block to be called when progress updates;
* first parameter is the number of completed (successful or not) requests,
* second parameter is the total number of images originally requested to be prefetched
* @param completionBlock block to be called when prefetching is completed
* first param is the number of completed (successful or not) requests,
* second parameter is the number of skipped requests
*/
- (void)prefetchURLs:(nullable NSArray *)urls
progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock;

/**
* Remove and cancel queued list
*/
- (void)cancelPrefetching;

## SDWebImagePrefetcher.m
@interface SDWebImagePrefetcher ()

@property (strong, nonatomic, nonnull) SDWebImageManager *manager;
@property (strong, nonatomic, nullable) NSArray *prefetchURLs;
@property (assign, nonatomic) NSUInteger requestedCount;
@property (assign, nonatomic) NSUInteger skippedCount;
@property (assign, nonatomic) NSUInteger finishedCount;
@property (assign, nonatomic) NSTimeInterval startedTime;
@property (copy, nonatomic, nullable) SDWebImagePrefetcherCompletionBlock completionBlock;
@property (copy, nonatomic, nullable) SDWebImagePrefetcherProgressBlock progressBlock;

@end
这里多了一个`skippedCount`属性,这个属性用来记录下载失败的次数,`skip`表示跳过的意思。

+ (nonnull instancetype)sharedImagePrefetcher {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}

- (nonnull instancetype)init {
return [self initWithImageManager:[SDWebImageManager new]];
}

- (nonnull instancetype)initWithImageManager:(SDWebImageManager *)manager {
if ((self = [super init])) {
_manager = manager;
_options = SDWebImageLowPriority;
_prefetcherQueue = dispatch_get_main_queue();
self.maxConcurrentDownloads = 3;
}
return self;
}

-

- (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads {
self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads;
}

- (NSUInteger)maxConcurrentDownloads {
return self.manager.imageDownloader.maxConcurrentDownloads;
}
这里是setter和getter方法,有意思的是setter并不以一定要给这个属性赋值,getter而不一定就一定返回该属性的值。

- (void)prefetchURLs:(nullable NSArray *)urls
progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock {
[self cancelPrefetching]; // Prevent duplicate prefetch request
self.startedTime = CFAbsoluteTimeGetCurrent();
self.prefetchURLs = urls;
self.completionBlock = completionBlock;
self.progressBlock = progressBlock;

if (urls.count == 0) {
if (completionBlock) {
completionBlock(0,0);
}
} else {
// Starts prefetching from the very first image on the list with the max allowed concurrency
NSUInteger listCount = self.prefetchURLs.count;
for (NSUInteger i = 0; i = self.prefetchURLs.count) return;
/// 已请求的个数加1
self.requestedCount++;
/// 使用self.manager下载图片
[self.manager loadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
/// 只有当finished完成之后,self.finishedCount加1
if (!finished) return;
self.finishedCount++;

if (image) { // 下载成功后,调用progressBlock
if (self.progressBlock) {
self.progressBlock(self.finishedCount,(self.prefetchURLs).count);
}
}
else { // 下载失败,也调用progressBlock,同时记录该次的下载失败
if (self.progressBlock) {
self.progressBlock(self.finishedCount,(self.prefetchURLs).count);
}
// Add last failed
self.skippedCount++;
}
/// 调用delegate
if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) {
[self.delegate imagePrefetcher:self
didPrefetchURL:self.prefetchURLs[index]
finishedCount:self.finishedCount
totalCount:self.prefetchURLs.count
];
}
/// 如果URLs的数量大于已经下载的数量,就说明还有没下载完的任务,继续下载下一个
if (self.prefetchURLs.count > self.requestedCount) {
dispatch_async(self.prefetcherQueue, ^{
[self startPrefetchingAtIndex:self.requestedCount];
});
} else if (self.finishedCount == self.requestedCount) { // 当完成数等于已请求总数的时候,就宣告下载完毕
/// 告诉代理,下载已经完毕
[self reportStatus];
/// 调用completionBlock,这里把completionBlock和progressBlock都设为nil是为了避免循环引用
if (self.completionBlock) {
self.completionBlock(self.finishedCount, self.skippedCount);
self.completionBlock = nil;
}
self.progressBlock = nil;
}
}];
}

- (void)reportStatus {
NSUInteger total = (self.prefetchURLs).count;
if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didFinishWithTotalCount:skippedCount:)]) {
[self.delegate imagePrefetcher:self
didFinishWithTotalCount:(total - self.skippedCount)
skippedCount:self.skippedCount
];
}
}

## 总结
`SDWebImagePrefetcher`是`SDWebImageManager`很好的应用例子,下一篇我们总结一下UI控件使用`SDWebImageManager`获取图片的例子。

**由于个人知识有限,如有错误之处,还望各路大侠给予指出啊**

1. SDWebImage源码解读 之 NSData+ImageContentType [简书](http://www.jianshu.com/p/fd984fd8bd5d) [博客园](http://www.cnblogs.com/machao/p/6126826.html)
2. SDWebImage源码解读 之 UIImage+GIF [简书](http://www.jianshu.com/p/d3e9e3d0a778) [博客园](http://www.cnblogs.com/machao/p/6134364.html)
3. SDWebImage源码解读 之 SDWebImageCompat [简书](http://www.jianshu.com/p/1d2e4d822732) [博客园](http://www.cnblogs.com/machao/p/6137517.html)
4. SDWebImage源码解读 之SDWebImageDecoder [简书](http://www.jianshu.com/p/9322acb7a7b1) [博客园](http://www.cnblogs.com/machao/p/6150636.html)
5. SDWebImage源码解读 之SDWebImageCache(上) [简书](http://www.jianshu.com/p/b3eff8304b37) [博客园](http://www.cnblogs.com/machao/p/6179638.html)
6. SDWebImage源码解读之SDWebImageCache(下) [简书](http://www.jianshu.com/p/a33d5abf686b) [博客园](http://www.cnblogs.com/machao/p/6198140.html)
7. SDWebImage源码解读之SDWebImageDownloaderOperation [简书](http://www.jianshu.com/p/662c09582d30) [博客园](http://www.cnblogs.com/machao/p/6248111.html)
8. SDWebImage源码解读之SDWebImageDownloader [简书](http://www.jianshu.com/p/8411e4645f0d) [博客园](http://www.cnblogs.com/machao/p/6265621.html)
9. SDWebImage源码解读之SDWebImageManager [简书](http://www.jianshu.com/p/a2cc208ee016) [博客园](http://www.cnblogs.com/machao/p/6323337.html)

SDWebImage源码解读之SDWebImagePrefetcher的更多相关文章

  1. SDWebImage源码解读之分类

    第十一篇 前言 我们知道SDWebImageManager是用来管理图片下载的,但我们平时的开发更多的是使用UIImageView和UIButton这两个控件显示图片. 按照正常的想法,我们只需要在他 ...

  2. SDWebImage源码解读之干货大总结

    这是我认为的一些重要的知识点进行的总结. 1.图片编码简介 大家都知道,数据在网络中是以二进制流的形式传播的,那么我们该如何把那些1和0解析成我们需要的数据格式呢? 说的简单一点就是,当文件都使用二进 ...

  3. SDWebImage源码解读之SDWebImageManager

    第九篇 前言 SDWebImageManager是SDWebImage中最核心的类了,但是源代码确是非常简单的.之所以能做到这一点,都归功于功能的良好分类. 有了SDWebImageManager这个 ...

  4. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  5. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  6. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

  7. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

  8. SDWebImage源码解读_之SDWebImageDecoder

    第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...

  9. SDWebImage源码解读之SDWebImageCache(上)

    第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...

随机推荐

  1. IOS中集合视图UICollectionView中DecorationView的简易使用方法

    转载自:   http://www.it165.net/pro/html/201312/8575.html Decoration View是UICollectionView的装饰视图.苹果官方给的案例 ...

  2. PHP 实现冒泡法排序

    <?php $nums = array(10,0,20,30,50,40,80); function stor($numbers){ $length = count($numbers); for ...

  3. CodeForces 629C Famil Door and Brackets

    DP. 具体做法:dp[i][j]表示长度为 i 的括号串,前缀和(左括号表示1,右括号表示-1)为 j 的有几种. 状态转移很容易得到:dp[i][j]=dp[i - 1][j + 1]+dp[i ...

  4. lpc1768ADC使用

    Lpc1768内置有一个ad外设,该外设有八路复用输入,所以,可以同时接八路ad设备,同时还支持触发转换模式,由外部端口进行ad触发,ad转换完成之后可以产生中断 Lpc1768支持的转换模式有两种, ...

  5. P4语言编程快速开始 实践二

    参考:P4语言编程快速开始 上一篇系列博客:P4语言编程快速开始 实践二 Demo 2 本Demo所做的修改及实现的功能: 为simple_router添加一个计数器(counter),该计数器附加( ...

  6. 1602A液晶

    液晶显示屏中,1602型算是比较简单的一种,据说和12864还是全兼容的.这两天学习的结果如下.一.1602里的存储器有三种:CGROM.CGRAM.DDRAM.CGROM保存了厂家生产时固化在LCM ...

  7. Spark Standalone Mode

    It is very easy to install a Spark cluster (Standalone mode). In my example, I used three machines. ...

  8. Tomcat用法--Servlet开发

    本来想玩一下微信公众号开发,没想到学习曲线真是恶心的很,首先你要配置Tomcat--你要会Servlet--你要会JSP,妈的!贴出来 快哭瞎了,各种文档,好吧,提供一个教你搞java EE文档的博客 ...

  9. jQuery初始化

    jQuery 初始化的理解 var jQuery = function( selector, context ) { // The jQuery object is actually just the ...

  10. 把C#对象变成数组技术---索引器(indexer)

    public class IndexerDemo { IList list = new List(); public IndexerDemo() { list.Add("); list.Ad ...