> 第十篇

## 前言
我们先看看`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. awesome-deep-learning

    https://github.com/ChristosChristofidis/awesome-deep-learning

  2. astah* professional 6.9.0

    下载地址:http://members.change-vision.com/files/astah_professional/6_9_0 破解方法:按照Astah Professional 6.9后打 ...

  3. mysql 插入数据后返回当前的自增ID方法

    存储过程的写法: mysql>create procedure test( ->in username varchar(50), ->in password varchar(50), ...

  4. POJ 1584 A Round Peg in a Ground Hole

    先判断是不是N多边形,求一下凸包,如果所有点都用上了,那么就是凸多边形 判断圆是否在多边形内, 先排除圆心在多边形外的情况 剩下的情况可以利用圆心到每条边的最短距离与半径的大小来判断 #include ...

  5. sencha touch视频教程

    链接地址:http://v.youku.com/v_show/id_XOTI1MDg1ODQ4.html

  6. (简单) POJ 1195 Mobile phones,二维树状数组。

    Description Suppose that the fourth generation mobile phone base stations in the Tampere area operat ...

  7. (中等) HDU 1043 Eight,经典搜索问题。

    Problem Description The 15-puzzle has been around for over 100 years; even if you don't know it by t ...

  8. Protocol Buffers

    今天来介绍一下"Protocol Buffers"(以下简称protobuf)这个玩意儿.本来俺在构思"生产者/消费者模式"系列的下一个帖子:关于生产者和消费者 ...

  9. 4、手把手教你Extjs5(四)主界面上加入顶部和底部区域

    这一节为主界面加一个顶部区域和底部区域.一个管理系统的界面可以粗分为顶部标题部分.中间数据展示和处理的部分.底部备注和状态部分. 在增加这二个区域之前,我们先在MainModel.js中加入一些数据. ...

  10. laravel5 数据库连接问题

    [PDOException] SQLSTATE[28000] [1045] Access denied for user ‘homestead’@’localhost’ (using password ...