SDWebImageManager主要關(guān)聯(lián)SDWebImageDownloader和SDImageCache操作,即異步下載圖片后進(jìn)行緩存處理。
SDWebImageOptions說明:
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #1d9421}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; min-height: 16.0px}p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #c91b13}p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo}p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px 'PingFang SC'; color: #1d9421}p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #c32275}span.s1 {font-variant-ligatures: no-common-ligatures}span.s2 {font-variant-ligatures: no-common-ligatures; color: #822e0e}span.s3 {font-variant-ligatures: no-common-ligatures; color: #c32275}span.s4 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures}span.s5 {font-variant-ligatures: no-common-ligatures; color: #000000}span.s6 {font: 14.0px 'PingFang SC'; font-variant-ligatures: no-common-ligatures}span.s7 {font-variant-ligatures: no-common-ligatures; color: #c91b13}span.s8 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #000000}span.s9 {font-variant-ligatures: no-common-ligatures; color: #0435ff}span.s10 {font-variant-ligatures: no-common-ligatures; color: #1d9421}span.s11 {font: 14.0px 'PingFang SC'; font-variant-ligatures: no-common-ligatures; color: #1d9421}
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageManager.h"
#import <objc/message.h>
@interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>
// 是否取消當(dāng)前所有操作
@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
// 沒有參數(shù)取消回調(diào)
@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock;
// 執(zhí)行緩存的操作
@property (strong, nonatomic) NSOperation *cacheOperation;
@end
@interface SDWebImageManager ()
// 圖片緩存對(duì)象
@property (strong, nonatomic, readwrite) SDImageCache *imageCache;
// 圖片下載對(duì)象
@property (strong, nonatomic, readwrite) SDWebImageDownloader *imageDownloader;
// 下載失敗的圖片URL
@property (strong, nonatomic) NSMutableSet *failedURLs;
// 裝載正在執(zhí)行操作的數(shù)組
@property (strong, nonatomic) NSMutableArray *runningOperations;
@end
@implementation SDWebImageManager
/**
* 生成一個(gè)SDWebImagemanager的單例
*
* @return <#return value description#>
*/
+ (id)sharedManager {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
/**
* 初始化SDImageCache杖玲、SDWebImageDownloader
*
* @return <#return value description#>
*/
- (instancetype)init {
SDImageCache *cache = [SDImageCache sharedImageCache];
SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
return [self initWithCache:cache downloader:downloader];
}
/**
* 初始化SDImageCache、SDWebImageDownloader
*
* @return <#return value description#>
*/
- (instancetype)initWithCache:(SDImageCache *)cache downloader:(SDWebImageDownloader *)downloader {
if ((self = [super init])) {
_imageCache = cache;
_imageDownloader = downloader;
_failedURLs = [NSMutableSet new];
_runningOperations = [NSMutableArray new];
}
return self;
}
/**
* 根絕URL獲取緩存中的key
*
* @param url 圖片URL
*
* @return 圖片在緩存中對(duì)應(yīng)的Key
*/
- (NSString *)cacheKeyForURL:(NSURL *)url {
if (!url) {
return @"";
}
if (self.cacheKeyFilter) {
return self.cacheKeyFilter(url);
} else {
return [url absoluteString];
}
}
/**
* 根據(jù)URL判斷緩存中是否存在圖片:先判斷內(nèi)存緩存铁追、在判斷磁盤緩存
*
* @param url 圖片URL
*
* @return 是否存在圖片
*/
- (BOOL)cachedImageExistsForURL:(NSURL *)url {
NSString *key = [self cacheKeyForURL:url];
if ([self.imageCache imageFromMemoryCacheForKey:key] != nil) return YES;
return [self.imageCache diskImageExistsWithKey:key];
}
/**
* 根據(jù)URL判斷磁盤緩存中是否存在圖片
*
* @param url 圖片URL
*
* @return 是否存在圖片
*/
- (BOOL)diskImageExistsForURL:(NSURL *)url {
NSString *key = [self cacheKeyForURL:url];
return [self.imageCache diskImageExistsWithKey:key];
}
/**
* 根據(jù)URL判斷緩存中是否存在圖片并進(jìn)行回調(diào):先判斷內(nèi)存緩存姻成、在判斷磁盤緩存
*
* @param url 圖片URL
* @param completionBlock 圖片在緩存中是否存在回調(diào)
*/
- (void)cachedImageExistsForURL:(NSURL *)url
completion:(SDWebImageCheckCacheCompletionBlock)completionBlock {
NSString *key = [self cacheKeyForURL:url];
BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil);
if (isInMemoryCache) {
// 確保回調(diào)在主線程中執(zhí)行:making sure we call the completion block on the main queue
dispatch_async(dispatch_get_main_queue(), ^{
if (completionBlock) {
completionBlock(YES);
}
});
return;
}
// 該方法的回調(diào)已在主線程中執(zhí)行
[self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
// the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch
if (completionBlock) {
completionBlock(isInDiskCache);
}
}];
}
/**
* 根據(jù)URL判斷磁盤緩存中是否存在圖片并進(jìn)行回調(diào)
*
* @param url 圖片URL
* @param completionBlock 圖片在緩存中是否存在回調(diào)
*/
- (void)diskImageExistsForURL:(NSURL *)url
completion:(SDWebImageCheckCacheCompletionBlock)completionBlock {
NSString *key = [self cacheKeyForURL:url];
[self.imageCache diskImageExistsWithKey:key completion:^(BOOL isInDiskCache) {
// the completion block of checkDiskCacheForImageWithKey:completion: is always called on the main queue, no need to further dispatch
if (completionBlock) {
completionBlock(isInDiskCache);
}
}];
}
/**
* 根據(jù)URL下載圖片
*
* @param url 圖片URL
* @param options 下載圖片選項(xiàng):SDWebImageOptions
* @param progressBlock 圖片下載進(jìn)度回調(diào)
* @param completedBlock 圖片下載完成回調(diào)
*
* @return SDWebImageOperation
*/
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
options:(SDWebImageOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {
// completedBlock為空時(shí)昙楚,斷言將被執(zhí)行近速,程序Crash,并打印出斷言中的描述信息堪旧。Invoking this method without a completedBlock is pointless
NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");
// Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, XCode won't
// throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
// 確保傳進(jìn)來的URL類型為NSURL削葱,如果為NSString則進(jìn)行類型轉(zhuǎn)化
if ([url isKindOfClass:NSString.class]) {
url = [NSURL URLWithString:(NSString *)url];
}
// Prevents app crashing on argument type error like sending NSNull instead of NSURL
// 防止URL為空時(shí)程序崩潰
if (![url isKindOfClass:NSURL.class]) {
url = nil;
}
// 執(zhí)行圖片下載操作的集合
__block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
__weak SDWebImageCombinedOperation *weakOperation = operation;
// 圖片URL是否失敗
BOOL isFailedUrl = NO;
// 創(chuàng)建一個(gè)互斥鎖,保證此時(shí)沒有其它線程對(duì)self對(duì)象進(jìn)行修改
@synchronized (self.failedURLs) {
isFailedUrl = [self.failedURLs containsObject:url];
}
// URL長度為0淳梦,或 獲取該URL已下載失敗過且操作類型沒有禁用掉黑名單列表析砸,則該圖片就下載失敗,返回失敗block
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
// 在主線程中執(zhí)行回調(diào)
dispatch_main_sync_safe(^{
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
completedBlock(nil, error, SDImageCacheTypeNone, YES, url);
});
return operation;
}
// 創(chuàng)建一個(gè)互斥鎖谭跨,保證此時(shí)沒有其它線程對(duì)self對(duì)象進(jìn)行修改
@synchronized (self.runningOperations) {
// 將操作加入到當(dāng)前運(yùn)行操作的數(shù)組中
[self.runningOperations addObject:operation];
}
NSString *key = [self cacheKeyForURL:url];
// 根據(jù)key從緩存里面查找
operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {
// 操作被取消干厚,則從當(dāng)前運(yùn)行操作的數(shù)組中刪除該操作
if (operation.isCancelled) {
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
return;
}
// 沒有圖片或操作類型會(huì)刷新緩存 且 delegate允許下載圖片
if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
// // 有圖片 并且操作類型為 SDWebImageRefreshCached(先用緩存圖片回調(diào)block李滴,圖片下載完成后再回調(diào)block)
if (image && options & SDWebImageRefreshCached) {
dispatch_main_sync_safe(^{
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
completedBlock(image, nil, cacheType, YES, url);
});
}
// download if no image or requested to refresh anyway, and download allowed by delegate
// 如果緩存沒有圖片或請(qǐng)求刷新,并通過委托下載圖片蛮瞄,則下載圖片
SDWebImageDownloaderOptions downloaderOptions = 0;
if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
if (image && options & SDWebImageRefreshCached) {
// force progressive off if image already cached but forced refreshing
downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
// ignore image read from NSURLCache if image if cached but force refreshing
downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
}
// 下載圖片
id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {
__strong __typeof(weakOperation) strongOperation = weakOperation;
if (!strongOperation || strongOperation.isCancelled) {
// Do nothing if the operation was cancelled
// See #699 for more details
// if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
// 操作取消則不做任何處理
// 如果這里進(jìn)行了completedBlock回調(diào)所坯,由于對(duì)同一個(gè)對(duì)象是競(jìng)爭(zhēng)條件,可能會(huì)覆蓋新數(shù)據(jù)挂捅?芹助??闲先?状土?
}
else if (error) {
// 出錯(cuò)
dispatch_main_sync_safe(^{
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(nil, error, SDImageCacheTypeNone, finished, url);
}
});
if ( error.code != NSURLErrorNotConnectedToInternet
&& error.code != NSURLErrorCancelled
&& error.code != NSURLErrorTimedOut
&& error.code != NSURLErrorInternationalRoamingOff
&& error.code != NSURLErrorDataNotAllowed
&& error.code != NSURLErrorCannotFindHost
&& error.code != NSURLErrorCannotConnectToHost) {
// 下載失敗圖片的URL添加到失敗URL數(shù)組
@synchronized (self.failedURLs) {
[self.failedURLs addObject:url];
}
}
}
// 下載成功
else {
//
if ((options & SDWebImageRetryFailed)) {
// 操作類型為失敗重新刷新下載,失敗圖片的URL添加到失敗URL數(shù)組
@synchronized (self.failedURLs) {
[self.failedURLs removeObject:url];
}
}
// 是否存儲(chǔ)在磁盤
BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
if (options & SDWebImageRefreshCached && image && !downloadedImage) {
// Image refresh hit the NSURLCache cache, do not call the completion block
}
// 圖片下載成功且需要轉(zhuǎn)換圖片
else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
// 在全局隊(duì)列中異步進(jìn)行操作
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// 根據(jù)代理獲取轉(zhuǎn)換后的圖片
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
// 轉(zhuǎn)換圖片存在 并且下載圖片操作已完成 則存儲(chǔ)圖片
if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
[self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
}
// completedBlock回調(diào)
dispatch_main_sync_safe(^{
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
}
});
});
}
// 存儲(chǔ)圖片并completedBlock回調(diào)
else {
if (downloadedImage && finished) {
[self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
}
dispatch_main_sync_safe(^{
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
}
});
}
}
// 圖片下載完成伺糠,則在運(yùn)行的操作數(shù)組中刪除該操作
if (finished) {
@synchronized (self.runningOperations) {
if (strongOperation) {
[self.runningOperations removeObject:strongOperation];
}
}
}
}];
// 取消操作,從數(shù)組中刪除該操作
operation.cancelBlock = ^{
[subOperation cancel];
@synchronized (self.runningOperations) {
__strong __typeof(weakOperation) strongOperation = weakOperation;
if (strongOperation) {
[self.runningOperations removeObject:strongOperation];
}
}
};
}
// 有圖片且線程沒有被取消,則返回有圖片的completedBlock
else if (image) {
dispatch_main_sync_safe(^{
__strong __typeof(weakOperation) strongOperation = weakOperation;
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(image, nil, cacheType, YES, url);
}
});
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
}
// 圖片沒有在緩存且不允許委托方法下載圖片
else {
// Image not in cache and download disallowed by delegate
dispatch_main_sync_safe(^{
__strong __typeof(weakOperation) strongOperation = weakOperation;
if (strongOperation && !weakOperation.isCancelled) {
completedBlock(nil, nil, SDImageCacheTypeNone, YES, url);
}
});
@synchronized (self.runningOperations) {
[self.runningOperations removeObject:operation];
}
}
}];
return operation;
}
/**
* 將圖片存入緩存
*
* @param image 圖片
* @param url 圖片URL
*/
- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url {
if (image && url) {
NSString *key = [self cacheKeyForURL:url];
[self.imageCache storeImage:image forKey:key toDisk:YES];
}
}
/**
* 取消掉當(dāng)前所有的下載圖片的operation
*/
- (void)cancelAll {
@synchronized (self.runningOperations) {
NSArray *copiedOperations = [self.runningOperations copy];
[copiedOperations makeObjectsPerformSelector:@selector(cancel)];
[self.runningOperations removeObjectsInArray:copiedOperations];
}
}
/**
* 檢查是否有一個(gè)或者多個(gè)operation正在執(zhí)行(簡單來說就是check是否有圖片在下載)
*
* @return <#return value description#>
*/
- (BOOL)isRunning {
BOOL isRunning = NO;
@synchronized(self.runningOperations) {
isRunning = (self.runningOperations.count > 0);
}
return isRunning;
}
@end
@implementation SDWebImageCombinedOperation
- (void)setCancelBlock:(SDWebImageNoParamsBlock)cancelBlock {
// check if the operation is already cancelled, then we just call the cancelBlock
if (self.isCancelled) {
if (cancelBlock) {
cancelBlock();
}
_cancelBlock = nil; // don't forget to nil the cancelBlock, otherwise we will get crashes
} else {
_cancelBlock = [cancelBlock copy];
}
}
- (void)cancel {
self.cancelled = YES;
if (self.cacheOperation) {
[self.cacheOperation cancel];
self.cacheOperation = nil;
}
if (self.cancelBlock) {
self.cancelBlock();
// TODO: this is a temporary fix to #809.
// Until we can figure the exact cause of the crash, going with the ivar instead of the setter
// self.cancelBlock = nil;
_cancelBlock = nil;
}
}
@end
@implementation SDWebImageManager (Deprecated)
// 廢棄的方法:deprecated method, uses the non deprecated method
// adapter for the completion block
- (id <SDWebImageOperation>)downloadWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletedWithFinishedBlock)completedBlock {
return [self downloadImageWithURL:url
options:options
progress:progressBlock
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (completedBlock) {
completedBlock(image, error, cacheType, finished);
}
}];
}
@end
SDWebImagePrefecher注釋:
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #1d9421}p.p2 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; min-height: 16.0px}p.p3 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #c91b13}p.p4 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo}p.p5 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px 'PingFang SC'; color: #1d9421}p.p6 {margin: 0.0px 0.0px 0.0px 0.0px; font: 14.0px Menlo; color: #c32275}span.s1 {font-variant-ligatures: no-common-ligatures}span.s2 {font-variant-ligatures: no-common-ligatures; color: #822e0e}span.s3 {font-variant-ligatures: no-common-ligatures; color: #c32275}span.s4 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures}span.s5 {font: 14.0px 'PingFang SC'; font-variant-ligatures: no-common-ligatures}span.s6 {font-variant-ligatures: no-common-ligatures; color: #0435ff}span.s7 {font-variant-ligatures: no-common-ligatures; color: #000000}span.s8 {font-variant-ligatures: no-common-ligatures; color: #1d9421}span.s9 {font: 14.0px 'PingFang SC'; font-variant-ligatures: no-common-ligatures; color: #1d9421}span.s10 {font: 14.0px Menlo; font-variant-ligatures: no-common-ligatures; color: #000000}
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImagePrefetcher.h"
@interface SDWebImagePrefetcher ()
@property (strong, nonatomic) SDWebImageManager *manager;
// 預(yù)取圖片地址數(shù)組
@property (strong, nonatomic) NSArray *prefetchURLs;
// 請(qǐng)求數(shù)
@property (assign, nonatomic) NSUInteger requestedCount;
@property (assign, nonatomic) NSUInteger skippedCount;
// 完成數(shù)
@property (assign, nonatomic) NSUInteger finishedCount;
@property (assign, nonatomic) NSTimeInterval startedTime;
@property (copy, nonatomic) SDWebImagePrefetcherCompletionBlock completionBlock;
@property (copy, nonatomic) SDWebImagePrefetcherProgressBlock progressBlock;
@end
@implementation SDWebImagePrefetcher
+ (SDWebImagePrefetcher *)sharedImagePrefetcher {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
- (id)init {
return [self initWithImageManager:[SDWebImageManager new]];
}
- (id)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;
}
/**
* 開始預(yù)取第幾張圖片
*
* @param index 圖片位置
*/
- (void)startPrefetchingAtIndex:(NSUInteger)index {
if (index >= self.prefetchURLs.count) return;
// 請(qǐng)求數(shù)+1
self.requestedCount++;
[self.manager downloadImageWithURL:self.prefetchURLs[index] options:self.options progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (!finished) return;
// 完成數(shù)+1
self.finishedCount++;
if (image) {
if (self.progressBlock) {
self.progressBlock(self.finishedCount,[self.prefetchURLs count]);
}
}
else {
if (self.progressBlock) {
self.progressBlock(self.finishedCount,[self.prefetchURLs count]);
}
// 圖片沒有下載完蒙谓,跳過數(shù)+1:Add last failed
self.skippedCount++;
}
if ([self.delegate respondsToSelector:@selector(imagePrefetcher:didPrefetchURL:finishedCount:totalCount:)]) {
[self.delegate imagePrefetcher:self
didPrefetchURL:self.prefetchURLs[index]
finishedCount:self.finishedCount
totalCount:self.prefetchURLs.count
];
}
if (self.prefetchURLs.count > self.requestedCount) {
// 預(yù)取圖片數(shù) > 請(qǐng)求數(shù),繼續(xù)預(yù)取圖片
dispatch_async(self.prefetcherQueue, ^{
[self startPrefetchingAtIndex:self.requestedCount];
});
} else if (self.finishedCount == self.requestedCount) {
// 完成數(shù) = 請(qǐng)求數(shù)训桶,該圖片下載完成累驮,進(jìn)行回調(diào)
[self reportStatus]; // 報(bào)告圖片完成狀態(tài)
if (self.completionBlock) {
self.completionBlock(self.finishedCount, self.skippedCount);
self.completionBlock = nil;
}
self.progressBlock = nil;
}
}];
}
/**
* 報(bào)告預(yù)取圖片完成狀態(tài)
*/
- (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
];
}
}
/**
* 根據(jù)圖片地址數(shù)組預(yù)取圖片
*
* @param urls 圖片地址數(shù)組
*/
- (void)prefetchURLs:(NSArray *)urls {
[self prefetchURLs:urls progress:nil completed:nil];
}
- (void)prefetchURLs:(NSArray *)urls progress:(SDWebImagePrefetcherProgressBlock)progressBlock completed:(SDWebImagePrefetcherCompletionBlock)completionBlock {
[self cancelPrefetching]; // 先取消圖片預(yù)取,防止重復(fù)操作: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 {
// 循環(huán)預(yù)取圖片: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];
}
}
}
/**
* 取消圖片預(yù)取
*/
- (void)cancelPrefetching {
self.prefetchURLs = nil;
self.skippedCount = 0;
self.requestedCount = 0;
self.finishedCount = 0;
[self.manager cancelAll];
}
@end