第十篇
前言
我們先看看SDWebImage
主文件的組成模塊:
可以看出來倦逐,每個模塊即獨立又相對關聯(lián),當最后拼接出SDWebImageManager
的時候城须,我們就可以利用它來做一些有意思的事情质和。
本篇就主要講解其中的一個使用場景:批量圖片下載火鼻。記得之前有一位同學有這樣的開發(fā)需求:他們公司要做一個漫畫APP隶垮,漫畫都是由圖片組成的藻雪,每一個本漫畫由很多章節(jié)組成,需要提供一個緩存功能狸吞,也就是把圖片一組一組的下載下來阔涉。那么使用本篇的這個類就能完美的解決它的需求。
SDWebImagePrefetcherDelegate
這個代理提供了兩個方法來監(jiān)聽事件:
- 每次下載完一個圖片
- 所有的都下載完
代碼:
@protocol SDWebImagePrefetcherDelegate <NSObject>
@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 <SDWebImagePrefetcherDelegate> 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<NSURL *> *)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<NSURL *> *)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<NSURL *> *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
屬性捷绒,這個屬性用來記錄下載失敗的次數(shù),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<NSURL *> *)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.maxConcurrentDownloads && self.requestedCount < listCount; i++) {
[self startPrefetchingAtIndex:i];
}
}
}
這個函數(shù)很有意思,首先調(diào)用了[self cancelPrefetching]
,我們看看該方法的實現(xiàn): (void)cancelPrefetching {
self.prefetchURLs = nil;
self.skippedCount = 0;
self.requestedCount = 0;
self.finishedCount = 0;
[self.manager cancelAll];
}
說明調(diào)用該方法后,所以的未完成的下載都會清空宙搬,也就是說SDWebImagePrefetcher
只專注處理一組URLs裆针,是無狀態(tài)的下載。也就要求我們傳入的URLs要完整跟狱。
那么我們?nèi)绾螌崿F(xiàn)支持多個圖片并發(fā)下載呢俭厚?我們都知道SDWebImageManager
的loadImage方法是異步執(zhí)行的,因此只要多次調(diào)用loadImage方法就能做到了驶臊。也就是[self startPrefetchingAtIndex:i];
- (void)startPrefetchingAtIndex:(NSUInteger)index {
/// 判斷index是否越界
if (index >= self.prefetchURLs.count) return;
/// 已請求的個數(shù)加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) { // 下載成功后叼丑,調(diào)用progressBlock
if (self.progressBlock) {
self.progressBlock(self.finishedCount,(self.prefetchURLs).count);
}
}
else { // 下載失敗,也調(diào)用progressBlock扛门,同時記錄該次的下載失敗
if (self.progressBlock) {
self.progressBlock(self.finishedCount,(self.prefetchURLs).count);
}
// Add last failed
self.skippedCount++;
}
/// 調(diào)用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的數(shù)量大于已經(jīng)下載的數(shù)量鸠信,就說明還有沒下載完的任務,繼續(xù)下載下一個
if (self.prefetchURLs.count > self.requestedCount) {
dispatch_async(self.prefetcherQueue, ^{
[self startPrefetchingAtIndex:self.requestedCount];
});
} else if (self.finishedCount == self.requestedCount) { // 當完成數(shù)等于已請求總數(shù)的時候论寨,就宣告下載完畢
/// 告訴代理星立,下載已經(jīng)完畢
[self reportStatus];
/// 調(diào)用completionBlock,這里把completionBlock和progressBlock都設為nil是為了避免循環(huán)引用
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
];
}
}
總結(jié)
SDWebImagePrefetcher
是SDWebImageManager
很好的應用例子,下一篇我們總結(jié)一下UI控件使用SDWebImageManager
獲取圖片的例子葬凳。
由于個人知識有限绰垂,如有錯誤之處,還望各路大俠給予指出啊
- SDWebImage源碼解讀 之 NSData+ImageContentType 簡書 博客園
- SDWebImage源碼解讀 之 UIImage+GIF 簡書 博客園
- SDWebImage源碼解讀 之 SDWebImageCompat 簡書 博客園
- SDWebImage源碼解讀 之SDWebImageDecoder 簡書 博客園
- SDWebImage源碼解讀 之SDWebImageCache(上) 簡書 博客園
- SDWebImage源碼解讀之SDWebImageCache(下) 簡書 博客園
- SDWebImage源碼解讀之SDWebImageDownloaderOperation 簡書 博客園
- SDWebImage源碼解讀之SDWebImageDownloader 簡書 博客園
- SDWebImage源碼解讀之SDWebImageManager 簡書 博客園