美文网首页
SDWebImage学习笔记(六):SDWebImageMana

SDWebImage学习笔记(六):SDWebImageMana

作者: 神采飞扬_2015 | 来源:发表于2016-07-18 10:06 被阅读182次

    SDWebImageManager主要关联SDWebImageDownloader和SDImageCache操作,即异步下载图片后进行缓存处理。

    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>
    
    // 是否取消当前所有操作
    @property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
    // 没有参数取消回调
    @property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock;
    // 执行缓存的操作
    @property (strong, nonatomic) NSOperation *cacheOperation;
    
    @end
    
    @interface SDWebImageManager ()
    
    // 图片缓存对象
    @property (strong, nonatomic, readwrite) SDImageCache *imageCache;
    // 图片下载对象
    @property (strong, nonatomic, readwrite) SDWebImageDownloader *imageDownloader;
    // 下载失败的图片URL
    @property (strong, nonatomic) NSMutableSet *failedURLs;
    // 装载正在执行操作的数组
    @property (strong, nonatomic) NSMutableArray *runningOperations;
    
    @end
    
    @implementation SDWebImageManager
    
    /**
     *  生成一个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 图片在缓存中对应的Key
     */
    - (NSString *)cacheKeyForURL:(NSURL *)url {
        if (!url) {
            return @"";
        }
        
        if (self.cacheKeyFilter) {
            return self.cacheKeyFilter(url);
        } else {
            return [url absoluteString];
        }
    }
    
    /**
     *  根据URL判断缓存中是否存在图片:先判断内存缓存、在判断磁盘缓存
     *
     *  @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];
    }
    
    /**
     *  根据URL判断磁盘缓存中是否存在图片
     *
     *  @param url 图片URL
     *
     *  @return 是否存在图片
     */
    - (BOOL)diskImageExistsForURL:(NSURL *)url {
        NSString *key = [self cacheKeyForURL:url];
        return [self.imageCache diskImageExistsWithKey:key];
    }
    
    /**
     *  根据URL判断缓存中是否存在图片并进行回调:先判断内存缓存、在判断磁盘缓存
     *
     *  @param url             图片URL
     *  @param completionBlock 图片在缓存中是否存在回调
     */
    - (void)cachedImageExistsForURL:(NSURL *)url
                         completion:(SDWebImageCheckCacheCompletionBlock)completionBlock {
        NSString *key = [self cacheKeyForURL:url];
        
        BOOL isInMemoryCache = ([self.imageCache imageFromMemoryCacheForKey:key] != nil);
        
        if (isInMemoryCache) {
            // 确保回调在主线程中执行:making sure we call the completion block on the main queue
            dispatch_async(dispatch_get_main_queue(), ^{
                if (completionBlock) {
                    completionBlock(YES);
                }
            });
            return;
        }
        
        // 该方法的回调已在主线程中执行
        [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);
            }
        }];
    }
    
    /**
     *  根据URL判断磁盘缓存中是否存在图片并进行回调
     *
     *  @param url             图片URL
     *  @param completionBlock 图片在缓存中是否存在回调
     */
    - (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);
            }
        }];
    }
    
    /**
     *  根据URL下载图片
     *
     *  @param url            图片URL
     *  @param options        下载图片选项:SDWebImageOptions
     *  @param progressBlock  图片下载进度回调
     *  @param completedBlock 图片下载完成回调
     *
     *  @return SDWebImageOperation
     */
    - (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                                             options:(SDWebImageOptions)options
                                            progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                           completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {
        // completedBlock为空时,断言将被执行,程序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.
        // 确保传进来的URL类型为NSURL,如果为NSString则进行类型转化
        if ([url isKindOfClass:NSString.class]) {
            url = [NSURL URLWithString:(NSString *)url];
        }
    
        // Prevents app crashing on argument type error like sending NSNull instead of NSURL
        // 防止URL为空时程序崩溃
        if (![url isKindOfClass:NSURL.class]) {
            url = nil;
        }
    
        // 执行图片下载操作的集合
        __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
        __weak SDWebImageCombinedOperation *weakOperation = operation;
    
        // 图片URL是否失败
        BOOL isFailedUrl = NO;
        // 创建一个互斥锁,保证此时没有其它线程对self对象进行修改
        @synchronized (self.failedURLs) {
            isFailedUrl = [self.failedURLs containsObject:url];
        }
    
        // URL长度为0,或 获取该URL已下载失败过且操作类型没有禁用掉黑名单列表,则该图片就下载失败,返回失败block
        if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
            // 在主线程中执行回调
            dispatch_main_sync_safe(^{
                NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
                completedBlock(nil, error, SDImageCacheTypeNone, YES, url);
            });
            return operation;
        }
    
        // 创建一个互斥锁,保证此时没有其它线程对self对象进行修改
        @synchronized (self.runningOperations) {
            // 将操作加入到当前运行操作的数组中
            [self.runningOperations addObject:operation];
        }
        NSString *key = [self cacheKeyForURL:url];
    
        // 根据key从缓存里面查找
        operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {
            // 操作被取消,则从当前运行操作的数组中删除该操作
            if (operation.isCancelled) {
                @synchronized (self.runningOperations) {
                    [self.runningOperations removeObject:operation];
                }
    
                return;
            }
    
            // 没有图片或操作类型会刷新缓存 且 delegate允许下载图片
            if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
                // // 有图片 并且操作类型为 SDWebImageRefreshCached(先用缓存图片回调block,图片下载完成后再回调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
                // 如果缓存没有图片或请求刷新,并通过委托下载图片,则下载图片
                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
                        // 操作取消则不做任何处理
                        // 如果这里进行了completedBlock回调,由于对同一个对象是竞争条件,可能会覆盖新数据?????
                    }
                    else if (error) {
                        // 出错
                        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数组
                            @synchronized (self.failedURLs) {
                                [self.failedURLs addObject:url];
                            }
                        }
                    }
                    // 下载成功
                    else {
                        //
                        if ((options & SDWebImageRetryFailed)) {
                            // 操作类型为失败重新刷新下载,失败图片的URL添加到失败URL数组
                            @synchronized (self.failedURLs) {
                                [self.failedURLs removeObject:url];
                            }
                        }
                        
                        // 是否存储在磁盘
                        BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
    
                        if (options & SDWebImageRefreshCached && image && !downloadedImage) {
                            // Image refresh hit the NSURLCache cache, do not call the completion block
                        }
                        // 图片下载成功且需要转换图片
                        else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
                            // 在全局队列中异步进行操作
                            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
                                // 根据代理获取转换后的图片
                                UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
    
                                // 转换图片存在 并且下载图片操作已完成 则存储图片
                                if (transformedImage && finished) {
                                    BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
                                    [self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
                                }
    
                                // completedBlock回调
                                dispatch_main_sync_safe(^{
                                    if (strongOperation && !strongOperation.isCancelled) {
                                        completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
                                    }
                                });
                            });
                        }
                        // 存储图片并completedBlock回调
                        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);
                                }
                            });
                        }
                    }
    
                    // 图片下载完成,则在运行的操作数组中删除该操作
                    if (finished) {
                        @synchronized (self.runningOperations) {
                            if (strongOperation) {
                                [self.runningOperations removeObject:strongOperation];
                            }
                        }
                    }
                }];
                // 取消操作,从数组中删除该操作
                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];
        }
    }
    
    /**
     *  取消掉当前所有的下载图片的operation
     */
    - (void)cancelAll {
        @synchronized (self.runningOperations) {
            NSArray *copiedOperations = [self.runningOperations copy];
            [copiedOperations makeObjectsPerformSelector:@selector(cancel)];
            [self.runningOperations removeObjectsInArray:copiedOperations];
        }
    }
    
    /**
     *  检查是否有一个或者多个operation正在执行(简单来说就是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;
    // 预取图片地址数组
    @property (strong, nonatomic) 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) 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;
    }
    
    /**
     *  开始预取第几张图片
     *
     *  @param index 图片位置
     */
    - (void)startPrefetchingAtIndex:(NSUInteger)index {
        if (index >= self.prefetchURLs.count) return;
        
        // 请求数+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;
            // 完成数+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]);
                }
                // 图片没有下载完,跳过数+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) {
                // 预取图片数 > 请求数,继续预取图片
                dispatch_async(self.prefetcherQueue, ^{
                    [self startPrefetchingAtIndex:self.requestedCount];
                });
            } else if (self.finishedCount == self.requestedCount) {
                // 完成数 = 请求数,该图片下载完成,进行回调
                [self reportStatus]; // 报告图片完成状态
                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
             ];
        }
    }
    
    /**
     *  根据图片地址数组预取图片
     *
     *  @param urls 图片地址数组
     */
    - (void)prefetchURLs:(NSArray *)urls {
        [self prefetchURLs:urls progress:nil completed:nil];
    }
    
    - (void)prefetchURLs:(NSArray *)urls progress:(SDWebImagePrefetcherProgressBlock)progressBlock completed:(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];
            }
        }
    }
    
    /**
     *  取消图片预取
     */
    - (void)cancelPrefetching {
        self.prefetchURLs = nil;
        self.skippedCount = 0;
        self.requestedCount = 0;
        self.finishedCount = 0;
        [self.manager cancelAll];
    }
    
    @end
    

    相关文章

      网友评论

          本文标题:SDWebImage学习笔记(六):SDWebImageMana

          本文链接:https://www.haomeiwen.com/subject/jfupjttx.html