该文章阅读的SDWebImage的版本为4.3.3。
该类是SDWebImageManager
类的一个应用——预加载:以较低优先级批量下载图像进行缓存,以供未来使用。
1.SDWebImagePrefetcherDelegate协议
/**
当一张图片已经预加载好的时候会调用这个方法
*/
- (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didPrefetchURL:(nullable NSURL *)imageURL finishedCount:(NSUInteger)finishedCount totalCount:(NSUInteger)totalCount;
复制代码
/**
当所有图片都已经预加载好的时候会调用这个方法
*/
- (void)imagePrefetcher:(nonnull SDWebImagePrefetcher *)imagePrefetcher didFinishWithTotalCount:(NSUInteger)totalCount skippedCount:(NSUInteger)skippedCount;
复制代码
2.公共类型定义
/**
定义用于回调进度情况的block
*/
typedef void(^SDWebImagePrefetcherProgressBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfTotalUrls);
复制代码
/**
定义用于回调完成情况的block
*/
typedef void(^SDWebImagePrefetcherCompletionBlock)(NSUInteger noOfFinishedUrls, NSUInteger noOfSkippedUrls);
复制代码
3.公共属性
/**
网络图像管理者对象
*/
@property (strong, nonatomic, readonly, nonnull) SDWebImageManager *manager;
复制代码
/**
最大图像预加载数量,默认为3张
*/
@property (nonatomic, assign) NSUInteger maxConcurrentDownloads;
复制代码
/**
图像加载选项,默认为SDWebImageLowPriority,也就是低优先级
*/
@property (nonatomic, assign) SDWebImageOptions options;
复制代码
/**
预加载所在的队列,默认是主队列
*/
@property (strong, nonatomic, nonnull) dispatch_queue_t prefetcherQueue;
复制代码
/**
代理对象
*/
@property (weak, nonatomic, nullable) id <SDWebImagePrefetcherDelegate> delegate;
复制代码
4.公共方法
/**
获取单例对象
*/
+ (nonnull instancetype)sharedImagePrefetcher;
复制代码
/**
以指定的网络图像管理者对象进行初始化
*/
- (nonnull instancetype)initWithImageManager:(nonnull SDWebImageManager *)manager NS_DESIGNATED_INITIALIZER;
复制代码
/**
预加载的图像url数组
*/
- (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls;
复制代码
/**
预加载的图像url数组,并回调进度和完成情况
*/
- (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls
progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock;
复制代码
/**
取消所有预加载任务
*/
- (void)cancelPrefetching;
复制代码
5.类扩展属性
/**
保存网络图像管理者对象
*/
@property (strong, nonatomic, nonnull) SDWebImageManager *manager;
复制代码
/**
保存预加载的图像url数组
*/
@property (strong, atomic, nullable) NSArray<NSURL *> *prefetchURLs;
复制代码
/**
保存请求的数量
*/
@property (assign, nonatomic) NSUInteger requestedCount;
复制代码
/**
保存跳过的数量
*/
@property (assign, nonatomic) NSUInteger skippedCount;
复制代码
/**
保存完成的数量
*/
@property (assign, nonatomic) NSUInteger finishedCount;
复制代码
/**
保存预加载开始的时间
*/
@property (assign, nonatomic) NSTimeInterval startedTime;
复制代码
/**
保存完成回调block
*/
@property (copy, nonatomic, nullable) SDWebImagePrefetcherCompletionBlock completionBlock;
复制代码
/**
保存进程回调block
*/
@property (copy, nonatomic, nullable) SDWebImagePrefetcherProgressBlock progressBlock;
复制代码
6.方法实现
6.1.生命周期方法实现
- (nonnull instancetype)init {
// 创建新的网络图像管理者对象初始化
return [self initWithImageManager:[SDWebImageManager new]];
}
复制代码
6.2.私有方法实现
/**
开始预加载指定索引的图像
*/
- (void)startPrefetchingAtIndex:(NSUInteger)index {
// 创建变量保存当前正在预加载的url
NSURL *currentURL;
// 加锁
@synchronized(self) {
// 如果想要预加载的索引超过了总共要预加载的数量,就返回不继续向下执行了
if (index >= self.prefetchURLs.count) return;
// 记录要预加载的url
currentURL = self.prefetchURLs[index];
// 记录请求数量
self.requestedCount++;
}
// 调用网络图像管理者对象加载图像
[self.manager loadImageWithURL:currentURL options:self.options progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
// 如果没有完成就返回
if (!finished) return;
// 记录完成数量
self.finishedCount++;
// 如果设置了进度回调block就回调进度情况
if (self.progressBlock) {
self.progressBlock(self.finishedCount,(self.prefetchURLs).count);
}
// 如果图像没有加载成功就记录跳过数量
if (!image) {
self.skippedCount++;
}
// 如果代理对象实现了代理方法,就调用传递完成情况
if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) {
[self.delegate imagePrefetcher:self
didPrefetchURL:currentURL
finishedCount:self.finishedCount
totalCount:self.prefetchURLs.count
];
}
if (self.prefetchURLs.count > self.requestedCount) {
// 如果总共要预加载的数量比请求的数量多
// 主队列异步加载
dispatch_async(self.prefetcherQueue, ^{
// 开始预加载下一个图像
[self startPrefetchingAtIndex:self.requestedCount];
});
} else if (self.finishedCount == self.requestedCount) {
// 如果完成的数量等于请求的数量
// 回调状态
[self reportStatus];
// 如果设置了完成回调block就回调完成情况
if (self.completionBlock) {
self.completionBlock(self.finishedCount, self.skippedCount);
// 置空保存完成回调block的属性
self.completionBlock = nil;
}
// 置空保存进度回调block的属性
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
];
}
}
复制代码
6.3.公共方法实现
+ (nonnull instancetype)sharedImagePrefetcher {
// 获取单例对象
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
复制代码
- (nonnull instancetype)initWithImageManager:(SDWebImageManager *)manager {
if ((self = [super init])) {
// 保存网络图像管理者对象
_manager = manager;
// 默认为低优先级
_options = SDWebImageLowPriority;
// 预加载队列为主队列
_prefetcherQueue = dispatch_get_main_queue();
// 最大预加载并发数为3
self.maxConcurrentDownloads = 3;
}
return self;
}
复制代码
- (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls {
// 调用下面的全能方法
[self prefetchURLs:urls progress:nil completed:nil];
}
复制代码
- (void)prefetchURLs:(nullable NSArray<NSURL *> *)urls
progress:(nullable SDWebImagePrefetcherProgressBlock)progressBlock
completed:(nullable SDWebImagePrefetcherCompletionBlock)completionBlock {
// 取消之前的预加载操作
[self cancelPrefetching];
// 获取当前时间
self.startedTime = CFAbsoluteTimeGetCurrent();
// 获取要预加载的url
self.prefetchURLs = urls;
// 保存完成回调block
self.completionBlock = completionBlock;
// 保存进度回调block
self.progressBlock = progressBlock;
if (urls.count == 0) {
// 如果没有要预加载的url就直接回调
if (completionBlock) {
completionBlock(0,0);
}
} else {
// 开启预加载
NSUInteger listCount = self.prefetchURLs.count;
for (NSUInteger i = 0; i < self.maxConcurrentDownloads && self.requestedCount < listCount; i++) {
[self startPrefetchingAtIndex:i];
}
}
}
复制代码
- (void)cancelPrefetching {
// 加锁
@synchronized(self) {
// 置空保存要预加载的url数组的属性
self.prefetchURLs = nil;
// 置空保存跳过数量的属性
self.skippedCount = 0;
// 置空保存请求数量的属性
self.requestedCount = 0;
// 置空保存完成数量的属性
self.finishedCount = 0;
}
// 调用网络图像管理者对象取消所有操作
[self.manager cancelAll];
}
复制代码
6.4.自定义getter/setter方法实现
- (void)setMaxConcurrentDownloads:(NSUInteger)maxConcurrentDownloads {
// 直接设置网络图像管理者对象的最大并发数
self.manager.imageDownloader.maxConcurrentDownloads = maxConcurrentDownloads;
}
复制代码
- (NSUInteger)maxConcurrentDownloads {
// 直接获取网络图像管理者对象的最大并发数
return self.manager.imageDownloader.maxConcurrentDownloads;
}
复制代码
7.总结
这个类利用前面对图像加载功能的良好封装,非常简洁的实现了预加载图像的功能。
源码阅读系列:SDWebImage
源码阅读:SDWebImage(二)——SDWebImageCompat
源码阅读:SDWebImage(三)——NSData+ImageContentType
源码阅读:SDWebImage(四)——SDWebImageCoder
源码阅读:SDWebImage(五)——SDWebImageFrame
源码阅读:SDWebImage(六)——SDWebImageCoderHelper
源码阅读:SDWebImage(七)——SDWebImageImageIOCoder
源码阅读:SDWebImage(八)——SDWebImageGIFCoder
源码阅读:SDWebImage(九)——SDWebImageCodersManager
源码阅读:SDWebImage(十)——SDImageCacheConfig
源码阅读:SDWebImage(十一)——SDImageCache
源码阅读:SDWebImage(十二)——SDWebImageDownloaderOperation
源码阅读:SDWebImage(十三)——SDWebImageDownloader
源码阅读:SDWebImage(十四)——SDWebImageManager
源码阅读:SDWebImage(十五)——SDWebImagePrefetcher
源码阅读:SDWebImage(十六)——SDWebImageTransition
源码阅读:SDWebImage(十七)——UIView+WebCacheOperation
源码阅读:SDWebImage(十八)——UIView+WebCache
源码阅读:SDWebImage(十九)——UIImage+ForceDecode/UIImage+GIF/UIImage+MultiFormat
源码阅读:SDWebImage(二十)——UIButton+WebCache
源码阅读:SDWebImage(二十一)——UIImageView+WebCache/UIImageView+HighlightedWebCache