• SDWebImage之SDWebImageManager


    SDWebImageManager是SDWebImage的核心类。它拥有一个SDWebImageCache和一个SDWebImageDownloader属性,分别用于图片的缓存和下载处理。虽然是核心类,但它的源码很简单,这是因为相应的功能职责进行了良好的分类。下面我们来看一下它的源码。

    1.SDWebImageOptions

    typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
        /**
         * By default, when a URL fail to be downloaded, the URL is blacklisted so the library won't keep trying.
         * This flag disable this blacklisting.
         */
        //默认情况下,当一个URL下载失败的时候,这个URL会被加入黑名单列表,下次再有这个url的请求则停止请求。如果为true,这个值表示需要再尝试请求。
        SDWebImageRetryFailed = 1 << 0,
    
        /**
         * By default, image downloads are started during UI interactions, this flags disable this feature,
         * leading to delayed download on UIScrollView deceleration for instance.
         */
        //默认情况下,当UI可以交互的时候就开始加载图片。这个标记可以阻止这个时候加载。而是当UIScrollView开始减速滑动的时候开始加载。
        SDWebImageLowPriority = 1 << 1,
    
        /**
         * This flag disables on-disk caching
         */
        //这个属性禁止磁盘缓存
        SDWebImageCacheMemoryOnly = 1 << 2,
    
        /**
         * This flag enables progressive download, the image is displayed progressively during download as a browser would do.
         * By default, the image is only displayed once completely downloaded.
         */
        //这个标记允许图片在加载过程中显示,就像浏览器那样。默认情况下,图片只会在加载完成以后再显示。
        SDWebImageProgressiveDownload = 1 << 3,
    
        /**
         * Even if the image is cached, respect the HTTP response cache control, and refresh the image from remote location if needed.
         * The disk caching will be handled by NSURLCache instead of SDWebImage leading to slight performance degradation.
         * This option helps deal with images changing behind the same request URL, e.g. Facebook graph api profile pics.
         * If a cached image is refreshed, the completion block is called once with the cached image and again with the final image.
         *
         * Use this flag only if you can't make your URLs static with embedded cache busting parameter.
         */
        /*
         *即使本地已经缓存了图片,但是根据HTTP的缓存策略去网络上加载图片。也就是说本地缓存了也不管了,尝试从网络上加载数据。但是具体是从代理加载、HTTP缓存加载、还是原始服务器加载这个就根据HTTP的请求头配置。
         *使用NSURLCache而不是SDWebImage来处理磁盘缓存。从而可能会导致轻微的性能损害。
         *这个选项专门用于处理,url地址没有变,但是url对应的图片数据在服务器改变的情况。
         *如果一个缓存图片更新了,则completion这个回调会被调用两次,一次返回缓存图片,一次返回最终图片。
         *我们只有在不能确保URL和它对应的内容不能完全对应的时候才使用这个标记。
         */
        SDWebImageRefreshCached = 1 << 4,
    
        /**
         * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for
         * extra time in background to let the request finish. If the background task expires the operation will be cancelled.
         */
        //当应用进入后台以后,图片继续下载。应用进入后台以后,通过向系统申请额外的时间来完成。如果时间超时,那么下载操作会被取消。
        SDWebImageContinueInBackground = 1 << 5,
    
        /**
         * Handles cookies stored in NSHTTPCookieStore by setting
         * NSMutableURLRequest.HTTPShouldHandleCookies = YES;
         */
        //处理缓存在`NSHTTPCookieStore`对象里面的cookie。通过设置`NSMutableURLRequest.HTTPShouldHandleCookies = YES`来实现的。
        SDWebImageHandleCookies = 1 << 6,
    
        /**
         * Enable to allow untrusted SSL certificates.
         * Useful for testing purposes. Use with caution in production.
         */
        //允许非信任的SSL证书请求。
        //在测试的时候很有用。但是正式环境要小心使用。
        SDWebImageAllowInvalidSSLCertificates = 1 << 7,
    
        /**
         * By default, images are loaded in the order in which they were queued. This flag moves them to
         * the front of the queue.
         */
        //默认情况下,图片加载的顺序是根据加入队列的顺序加载的。但是这个标记会把任务加入队列的最前面。
        SDWebImageHighPriority = 1 << 8,
        
        /**
         * By default, placeholder images are loaded while the image is loading. This flag will delay the loading
         * of the placeholder image until after the image has finished loading.
         */
        //默认情况下,在图片加载的过程中,会显示占位图。
        //但是这个标记会阻止显示占位图直到图片加载完成。
        SDWebImageDelayPlaceholder = 1 << 9,
    
        /**
         * We usually don't call transformDownloadedImage delegate method on animated images,
         * as most transformation code would mangle it.
         * Use this flag to transform them anyway.
         */
        //默认情况下,我们不会去调用`animated images`(估计就是多张图片循环显示或者GIF图片)的`transformDownloadedImage`代理方法来处理图片。因为大部分transformation操作会对图片做无用处理。
        //用这个标记表示无论如何都要对图片做transform处理。
        SDWebImageTransformAnimatedImage = 1 << 10,
        
        /**
         * By default, image is added to the imageView after download. But in some cases, we want to
         * have the hand before setting the image (apply a filter or add it with cross-fade animation for instance)
         * Use this flag if you want to manually set the image in the completion when success
         */
        //默认情况下,图片在下载完成以后都会被自动加载到UIImageView对象上面。但是有时我们希望UIImageView加载我们手动处理以后的图片。
        //这个标记允许我们在completion这个Block中手动设置处理好以后的图片。
        SDWebImageAvoidAutoSetImage = 1 << 11,
        
        /**
         * By default, images are decoded respecting their original size. On iOS, this flag will scale down the
         * images to a size compatible with the constrained memory of devices.
         * If `SDWebImageProgressiveDownload` flag is set the scale down is deactivated.
         */
        //默认情况下,图片会按照它的原始大小来解码显示。根据设备的内存限制,这个属性会调整图片的尺寸到合适的大小再解码。
        //如果`SDWebImageProgressiveDownload`标记被设置了,则这个flag不起作用。
        SDWebImageScaleDownLargeImages = 1 << 12
    };

    2.宏定义

    /**
     图片下载完成回调Block(外部使用,一般在我们常使用的视图分类方法中用)
    
     @param image 图片
     @param error 错误信息
     @param cacheType 图片获取方式(本地缓存;内存缓存;网络)
     @param imageURL 图片URL
     */
    typedef void(^SDExternalCompletionBlock)(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL);
    
    /**
     图片下载完成回调Block(内部使用,仅在本类中使用)
     
     @param image 图片,如果请求出错,那么image参数为nil
     @param data 图片数据
     @param error 错误信息
     @param cacheType 图片获取方式(本地缓存;内存缓存;网络)
     @param finished 是否下载完成
     @param imageURL 图片URL
     */
    typedef void(^SDInternalCompletionBlock)(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL);
    
    /**
     对URL格式做处理,在方法cacheKeyForURL:中使用
    
     @param url URL
     @return 处理之后的URL
     */
    typedef NSString * _Nullable (^SDWebImageCacheKeyFilterBlock)(NSURL * _Nullable url);

    3.协议SDWebImageManagerDelegate

    @protocol SDWebImageManagerDelegate <NSObject>
    
    @optional
    
    /**
     在缓存中没有找到图片,控制是否去下载图片
    
     @param imageManager SDWebImageManager
     @param imageURL 图片URL
     @return YES/NO
     */
    - (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL;
    
    /**
     图片下载完成,在保存到磁盘和内存之前,对图片进行转换(主要是针对动态图)
    
     @param imageManager SDWebImageManager
     @param image 图片
     @param imageURL 图片URL
     @return 转换之后的图片
     */
    - (nullable UIImage *)imageManager:(nonnull SDWebImageManager *)imageManager transformDownloadedImage:(nullable UIImage *)image withURL:(nullable NSURL *)imageURL;
    
    @end

    4.SDWebImageCombinedOperation

    SDWebImageCombinedOperation是对每一个下载任务的封装,它最重要的是提供了一个取消任务功能

    @interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>
    
    @property (assign, nonatomic, getter = isCancelled) BOOL cancelled;  //!<用于判断Operation是否已经取消
    @property (copy, nonatomic, nullable) SDWebImageNoParamsBlock cancelBlock;  //!<取消回调的Block
    @property (strong, nonatomic, nullable) NSOperation *cacheOperation;  //!<NSOperation对象。可以通过这个属性取消一个NSOperation
    
    @end
    
    @implementation SDWebImageCombinedOperation
    
    /**
     取消Operation的回调Block
    
     @param cancelBlock 回调Block
     */
    - (void)setCancelBlock:(nullable 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];
        }
    }
    
    /**
     这个方法继承自`SDWebImageOperation`协议,方法里面会调用`SDWebImageDownlaoderOperation`或者`NSOperation`的cancel方法
     */
    - (void)cancel {
        self.cancelled = YES;
        if (self.cacheOperation) {
            //调用`SDWebImageDownlaoderOperation`或者`NSOperation`的cancel方法
            [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

    5.SDWebImageManager的属性

    //.h文件
    @property (weak, nonatomic, nullable) id <SDWebImageManagerDelegate> delegate;  //!<代理
    
    @property (strong, nonatomic, readonly, nullable) SDImageCache *imageCache;  //!<缓存对象
    @property (strong, nonatomic, readonly, nullable) SDWebImageDownloader *imageDownloader;  //!<下载对象
    
    //.m文件
    @property (strong, nonatomic, readwrite, nonnull) SDImageCache *imageCache;  //!<重写为可读写的缓存对象
    @property (strong, nonatomic, readwrite, nonnull) SDWebImageDownloader *imageDownloader;  //!<重写为可读写的下载对象
    @property (strong, nonatomic, nonnull) NSMutableSet<NSURL *> *failedURLs;  //!<加载失败的URL
    @property (strong, nonatomic, nonnull) NSMutableArray<SDWebImageCombinedOperation *> *runningOperations;  //!<当前正在加载的任务

    6.SDWebImageManager的方法

    6.1初始化

    + (nonnull instancetype)sharedManager {
        static dispatch_once_t once;
        static id instance;
        dispatch_once(&once, ^{
            instance = [self new];
        });
        return instance;
    }
    
    - (nonnull instancetype)init {
        SDImageCache *cache = [SDImageCache sharedImageCache];
        SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
        return [self initWithCache:cache downloader:downloader];
    }
    
    /**
     初始化
    
     @param cache SDImageCache对象
     @param downloader SDWebImageDownloader对象
     @return 返回初始化结果
     */
    - (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader {
        if ((self = [super init])) {
            _imageCache = cache;
            _imageDownloader = downloader;
            //用于保存加载失败的url集合
            _failedURLs = [NSMutableSet new];
            //用于保存当前正在加载的Operation
            _runningOperations = [NSMutableArray new];
        }
        return self;
    }

    6.2缓存查询

    /**
     根据url获取url对应的缓存key。如果有实现指定的url转换key的Block,则用这个方式转换为key;否则直接用url的绝对路径作为key
    
     @param url url
     @return 缓存的key
     */
    - (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url {
        if (!url) {
            return @"";
        }
    
        if (self.cacheKeyFilter) {
            return self.cacheKeyFilter(url);
        } else {
            return url.absoluteString;
        }
    }
    
    /**
     url的缓存是否存在
    
     @param url 缓存数据对应的url
     @param completionBlock 缓存结果回调
     */
    - (void)cachedImageExistsForURL:(nullable NSURL *)url
                         completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
        NSString *key = [self cacheKeyForURL:url];
        //内存里面是否有key的缓存
        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:(nullable NSURL *)url
                       completion:(nullable 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);
            }
        }];
    }

    6.3下载(SDWebImage的核心方法)

    /**
     核心方法。UIView、UIImageView等分类都默认通过调用这个方法来获取数据
    
     @param url 图片的url地址
     @param options 获取图片加载处理的属性
     @param progressBlock 加载进度回调
     @param completedBlock 加载完成回调
     @return 返回一个加载的载体对象,以便提供给后面取消删除等
     */
    - (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(nullable SDInternalCompletionBlock)completedBlock {
        // Invoking this method without a completedBlock is pointless
        //如果想预先下载图片,使用[SDWebImagePrefetcher prefetchURLs]取代本方法。预下载图片是有很多种使用场景的,当我们使用SDWebImagePrefetcher下载图片后,之后使用该图片时就不用从网络上下载了。
        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是NSString格式的,则转换为NSURL类型再处理
        if ([url isKindOfClass:NSString.class]) {
            url = [NSURL URLWithString:(NSString *)url];
        }
    
        // Prevents app crashing on argument type error like sending NSNull instead of NSURL
        //如果url不是NSURL类型的对象,则置为nil
        if (![url isKindOfClass:NSURL.class]) {
            url = nil;
        }
        //图片加载获取获取过程中绑定一个`SDWebImageCombinedOperation`对象,以方便后续再通过这个对象对url的加载控制。
        __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
        __weak SDWebImageCombinedOperation *weakOperation = operation;
    
        BOOL isFailedUrl = NO;
        //当前url是否在失败url的集合里面。在图片的下载中,会有一些下载失败的情况,这时候会把这些下载失败的url放到一个集合中去,也就是加入了黑名单,默认是不会再继续下载黑名单中的url了,但是也有例外,当options被设置为SDWebImageRetryFailed的时候,会尝试进行重新下载。
        if (url) {
            @synchronized (self.failedURLs) {
                isFailedUrl = [self.failedURLs containsObject:url];
            }
        }
        //如果url长度为0 或者是 失败的url且图片加载处理属性不包含SDWebImageRetryFailed,则直接返回
        if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
            //构建回调Block
            [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
            return operation;
        }
        //把加载图片的operation存入runningOperations,里面是所有正在做图片加载过程的operation的集合
        @synchronized (self.runningOperations) {
            [self.runningOperations addObject:operation];
        }
        //根据url获取url对应的key
        NSString *key = [self cacheKeyForURL:url];
        //key为空或者图片从内存中加载,则返回的cacheOperation是nil;其他情况cacheOperation为NSOperation对象
        operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
            //如果已经取消了操作,则直接返回并且移除对应的opetation对象
            if (operation.isCancelled) {
                [self safelyRemoveOperationFromRunning:operation];
                return;
            }
            //(如果未能在缓存中找到图片||强制刷新缓存) && (代理中未实现图片是否下载的方法||代理中图片是否应该下载方法返回值为YES
            if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
                //如果从缓存中获取了图片并且设置了SDWebImageRefreshCached来忽略缓存,则先把缓存的图片返回
                if (cachedImage && options & SDWebImageRefreshCached) {
                    // 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.
                    [self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
                }
    
                // download if no image or requested to refresh anyway, and download allowed by delegate
                //把图片加载的`SDWebImageOptions`类型枚举转换为图片下载的`SDWebImageDownloaderOptions`类型的枚举
                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 (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages;
                //如果设置了强制刷新缓存的选项。则`SDWebImageDownloaderProgressiveDownload`选项失效并且添加`SDWebImageDownloaderIgnoreCachedResponse`选项
                if (cachedImage && 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;
                }
                //新建一个网络下载的操作
                SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, 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
                    } else if (error) {
                        //如果加载出错,则直接返回回调,并且添加到failedURLs中
                        [self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url];
    
                        if (   error.code != NSURLErrorNotConnectedToInternet
                            && error.code != NSURLErrorCancelled
                            && error.code != NSURLErrorTimedOut
                            && error.code != NSURLErrorInternationalRoamingOff
                            && error.code != NSURLErrorDataNotAllowed
                            && error.code != NSURLErrorCannotFindHost
                            && error.code != NSURLErrorCannotConnectToHost
                            && error.code != NSURLErrorNetworkConnectionLost) {
                            @synchronized (self.failedURLs) {
                                [self.failedURLs addObject:url];
                            }
                        }
                    }
                    else { //网络图片下载成功
                        //如果有重试失败下载的选项,则把url从failedURLS中移除
                        if ((options & SDWebImageRetryFailed)) {
                            @synchronized (self.failedURLs) {
                                [self.failedURLs removeObject:url];
                            }
                        }
                        //是否需要保存到磁盘
                        BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
                        //如果设置了强制刷新缓存 && 缓存图片存在 && 下载图片不存在,那么直接返回,不执行回调Block
                        if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
                            // Image refresh hit the NSURLCache cache, do not call the completion block
                        }
                        //如果成功下载图片 && 图片是动态图片 && 实现了imageManager:transformDownloadedImage:withURL:代理方法,则进行图片的处理
                        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), ^{
                                //获取transform以后的图片
                                UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
                                //存储transform以后的的图片
                                if (transformedImage && finished) {
                                    BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
                                    // pass nil if the image was transformed, so we can recalculate the data from the image
                                    [self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];
                                }
                                //回调拼接
                                [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
                            });
                        } else {
                            //如果成功下载图片,直接缓存和回调
                            if (downloadedImage && finished) {
                                [self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
                            }
                            //回调拼接
                            [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
                        }
                    }
                    //从正在加载的图片操作集合中移除当前操作
                    if (finished) {
                        [self safelyRemoveOperationFromRunning:strongOperation];
                    }
                }];
                //重置cancelBlock,取消下载operation
                operation.cancelBlock = ^{
                    [self.imageDownloader cancel:subOperationToken];
                    __strong __typeof(weakOperation) strongOperation = weakOperation;
                    [self safelyRemoveOperationFromRunning:strongOperation];
                };
            } else if (cachedImage) {  //如果获取到了缓存图片,则直接通过缓存图片处理
                __strong __typeof(weakOperation) strongOperation = weakOperation;
                [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
                [self safelyRemoveOperationFromRunning:operation];
            } else {  //图片没有缓存并且图片也没有下载
                // Image not in cache and download disallowed by delegate
                __strong __typeof(weakOperation) strongOperation = weakOperation;
                [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
                [self safelyRemoveOperationFromRunning:operation];
            }
        }];
    
        return operation;
    }

    6.4其他方法

    /**
     保存图片到缓存
    
     @param image 图片
     @param url URL
     */
    - (void)saveImageToCache:(nullable UIImage *)image forURL:(nullable NSURL *)url {
        if (image && url) {
            NSString *key = [self cacheKeyForURL:url];
            [self.imageCache storeImage:image forKey:key toDisk:YES completion:nil];
        }
    }
    
    /**
     取消所有的下载
     */
    - (void)cancelAll {
        @synchronized (self.runningOperations) {
            NSArray<SDWebImageCombinedOperation *> *copiedOperations = [self.runningOperations copy];
            [copiedOperations makeObjectsPerformSelector:@selector(cancel)];
            [self.runningOperations removeObjectsInArray:copiedOperations];
        }
    }
    
    /**
     查看是否正在执行任务
    
     @return YES/NO
     */
    - (BOOL)isRunning {
        BOOL isRunning = NO;
        @synchronized (self.runningOperations) {
            isRunning = (self.runningOperations.count > 0);
        }
        return isRunning;
    }
    
    /**
     安全移除任务
    
     @param operation 任务
     */
    - (void)safelyRemoveOperationFromRunning:(nullable SDWebImageCombinedOperation*)operation {
        @synchronized (self.runningOperations) {
            if (operation) {
                [self.runningOperations removeObject:operation];
            }
        }
    }
    
    /**
     拼接回调Block并进行回调
    
     @param operation 任务
     @param completionBlock 回调Block
     @param error error
     @param url URL
     */
    - (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation
                                 completion:(nullable SDInternalCompletionBlock)completionBlock
                                      error:(nullable NSError *)error
                                        url:(nullable NSURL *)url {
        [self callCompletionBlockForOperation:operation completion:completionBlock image:nil data:nil error:error cacheType:SDImageCacheTypeNone finished:YES url:url];
    }
    
    /**
     拼接回调Block并进行回调
    
     @param operation 任务
     @param completionBlock 回调Block
     @param image 图片
     @param data 图片数据
     @param error error
     @param cacheType 缓存类型
     @param finished 是否完成
     @param url URL
     */
    - (void)callCompletionBlockForOperation:(nullable SDWebImageCombinedOperation*)operation
                                 completion:(nullable SDInternalCompletionBlock)completionBlock
                                      image:(nullable UIImage *)image
                                       data:(nullable NSData *)data
                                      error:(nullable NSError *)error
                                  cacheType:(SDImageCacheType)cacheType
                                   finished:(BOOL)finished
                                        url:(nullable NSURL *)url {
        dispatch_main_async_safe(^{
            if (operation && !operation.isCancelled && completionBlock) {
                completionBlock(image, data, error, cacheType, finished, url);
            }
        });
    }
  • 相关阅读:
    hdu 4521 小明系列问题——小明序列(线段树 or DP)
    hdu 1115 Lifting the Stone
    hdu 5476 Explore Track of Point(2015上海网络赛)
    Codeforces 527C Glass Carving
    hdu 4414 Finding crosses
    LA 5135 Mining Your Own Business
    uva 11324 The Largest Clique
    hdu 4288 Coder
    PowerShell随笔3 ---别名
    PowerShell随笔2---初始命令
  • 原文地址:https://www.cnblogs.com/LeeGof/p/6929049.html
Copyright © 2020-2023  润新知