SDWebImage源码解读之SDWebImagePrefetcher
> 第十篇
## 前言
我们先看看`SDWebImage`主文件的组成模块:

可以看出来,每个模块即独立又相对关联,当最后拼接出`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的更多相关文章
- SDWebImage源码解读之分类
第十一篇 前言 我们知道SDWebImageManager是用来管理图片下载的,但我们平时的开发更多的是使用UIImageView和UIButton这两个控件显示图片. 按照正常的想法,我们只需要在他 ...
- SDWebImage源码解读之干货大总结
这是我认为的一些重要的知识点进行的总结. 1.图片编码简介 大家都知道,数据在网络中是以二进制流的形式传播的,那么我们该如何把那些1和0解析成我们需要的数据格式呢? 说的简单一点就是,当文件都使用二进 ...
- SDWebImage源码解读之SDWebImageManager
第九篇 前言 SDWebImageManager是SDWebImage中最核心的类了,但是源代码确是非常简单的.之所以能做到这一点,都归功于功能的良好分类. 有了SDWebImageManager这个 ...
- SDWebImage源码解读之SDWebImageDownloaderOperation
第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...
- SDWebImage源码解读 之 NSData+ImageContentType
第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...
- SDWebImage源码解读 之 UIImage+GIF
第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...
- SDWebImage源码解读 之 SDWebImageCompat
第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...
- SDWebImage源码解读_之SDWebImageDecoder
第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...
- SDWebImage源码解读之SDWebImageCache(上)
第五篇 前言 本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计.在架构上来说,缓存算是存储设计的一部分.我们把各种不同的存储内容按照功能进行切割后,图片缓 ...
随机推荐
- Nginx 虚拟主机下支持Pathinfo并隐藏入口文件的完整配置
server { listen 80; server_name zuqiu.com; # 设置你的域名 index index.html index.htm index.php; root D:/wn ...
- iOS下bound,center和frame
本文转发至:http://www.xuebuyuan.com/1846606.html 在写程序的时候发现,iOS下的坐标.位置很容易弄乱,特别是在不同的坐标系统中,必须完成弄明白一些概念才能做相应的 ...
- DMLC深度机器学习框架MXNet的编译安装
这篇文章将介绍MXNet的编译安装. MXNet的编译安装分为两步: 首先,从C++源码编译共享库(libmxnet.so for linux,libmxnet.dylib for osx,libmx ...
- HDU 2809 God of War
状压DP.我认为是数据水了,用打死了哪几只作为状态,AC代码只需要保存当前状态的最大血量,完全没有考虑攻击力大小. 个人认为正确DP应该这样的:dp[状态][等级],但这样写不能AC,时间复杂度会很大 ...
- iOS开发——浅谈构架与用户体验
工作不是千篇一律的重复,从中寻找乐趣才是我们应该做的. 作为一名码农,做过几个项目,每次做项目的时候都会自己构思,如果完全是我自己设计,会怎么去设计?心里一直没有满意的答案,不管怎么布局,好像都感觉差 ...
- STM32硬件IIC操作
Stm32具有IIC接口,接口有以下主要特性 多主机功能:该模块既可做主设备也可做从设备 主设备功能 C地址检测 位/10位地址和广播呼叫 支持不同的通讯速度 状态标志: 发送器/接收器模式标志 字节 ...
- unicode转GBK,GNK转unicode,解决FATFS中文码表占用ROM问题(转)
源:unicode转GBK,GNK转unicode,解决FATFS中文码表占用ROM问题 之前一直使用的512KB ROM的STM32,但是最近使用的只有128KB,想用FATFS显示支持长文件名,发 ...
- Java通过JNI调用dll详细过程(转)
源:Java通过JNI调用dll详细过程 最近项目有这样一个需求,在已有的CS软件中添加一个链接,将当前登录用户的用户名加密后放在url地址中,在BS的login方法里通过解密判断,如果为合法用户则无 ...
- listview 去掉header和footer中的分割线
在listView中加上android:headerDividersEnabled="false" android:footerDividersEnabled="fals ...
- onethink权限管理 RBAC
查看了官网的视频,写的不错. 大致熟悉了流程.这里打算自己动手写一个权限的控制