一体化框架结构,图片缓存

图形缓存是SDWebImage最要害,最常用的效用。顾客浏览过的图纸会暗中认可缓存在cache与磁盘上,缓存私下认可保存一星期时间。约等于说在无互连网的条件下,客商照旧能够透过缓存查看到一星期之内的已经浏览过的图纸。很庞大,不过它也是个吃磁盘的妖精,一张图片数据实际上会在磁盘上保留两份,能够想像磁盘可用空间减弱的速度有多快。提出接纳SDWebImage的开垦者给顾客提供四个去掉缓存的效应,使客商可以马上的清理磁盘,防止App被卸载的正剧。

SDWebImage总体协会

在日常的花色中,对于加载网络图片,大家少不了用到SDWebImage。
仅供给调用UIImage分类的一行代码(别的的加载图片分类方法其实最终也会调到该措施):

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock

SDWebImage会自动为我们成功加载cache,更新cache以及下载图片的法力,何况仅须求调用那三个接口。
SDWebImage是怎样成功这么些效能的吧?
源码前边无暧昧,这就让大家一并来研商学习SDWebImage。
先是,上一张SDWebImage的总体结构图:

澳门葡京备用网址 1

咱俩在采纳SDWebImage时,会调用UIImageView的分类方法,而在分拣方法里面,实际上会调用单例实例
SDWebImageManager,而对应每二个加载图片操作(这里所谓的加载,指读取缓存或下载图片,并更新缓存),都有三个SDWebImageCombinedOperation对象,注意那个目的类的名字中包涵的‘Combined’,注脚这些Operation,其实是读写缓存,下载文件五种操作的三个Combine。
SDWebImageManager中,又包罗三个只读属性
@property (strong, nonatomic, readonly) SDImageCache *imageCache;
@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader;
看名字就精晓,imageCache肩负Cache的读写及立异,而imageDownloader则担任互连网下载图片。
而在SDWebImageDownloader中,每三个下载操作,又对应于贰个SDWebImageDownloaderOperation对象。

从上的组织深入分析能够知晓,对于每一个操作,SDWebImage实际会抽象出二个operation对象,那样,就能够完毕对于同不平日间进行四个操作的一个管制,也等于对operation对象的管住。

新近做了三个变越来越快照的要求,个中用到 SDWebImage
来下载图片,在采纳该框架的进度中也遭受了部分题目,索性正好就把
SDWebImage
(v3.7.3)
源码细读了一晃,学习一下里边的安顿观念和技能点,为了梳理思路,顺便写下了这篇小说。
目录
简介

话说想要提高必须站在巨人的肩膀上,最近没事就研究一下SDWebImage的源码

SDWebImageManager类是SDWebImage的中坚类,它管理着图片的下一步具体行为:

一张图片是哪些加载到UI的

看完上边SDWebImage的总体架构图,大家再结合实际代码,来寻访一个网络图片,是何等下载到本地的。
上面是UIImageView (WebCache)加载图片的章程实现:

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
    // step1. 先关掉之前的下载
    [self sd_cancelCurrentImageLoad];

    // step2. 利用runtime, 存储当前下载的URL
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    if (!(options & SDWebImageDelayPlaceholder)) { // step3. 设置placeholder
        dispatch_main_async_safe(^{  // dispatch_main_async_safe 宏 用于确保在mainqueue 上执行block
            self.image = placeholder;
        });
    }

    // step4. 进入图片 下载/读缓存 处理
    if (url) {

        // check if activityView is enabled or not
        if ([self showActivityIndicatorView]) {
            [self addActivityIndicator];
        }

        __weak __typeof(self)wself = self;
        // step4.1 调用 SDWebImageManager 单例下载
        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
            [wself removeActivityIndicator];
            if (!wself) return;  // 由于是block回调 这时weakself 可能已经释放,若释放,则return,不做任何回调处理
            dispatch_main_sync_safe(^{
                if (!wself) return;
                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
                {
                    completedBlock(image, error, cacheType, url);
                    return;
                }
                else if (image) {
                    wself.image = image;
                    [wself setNeedsLayout];
                } else {
                    if ((options & SDWebImageDelayPlaceholder)) {
                        wself.image = placeholder;
                        [wself setNeedsLayout];
                    }
                }
                if (completedBlock && finished) {
                    completedBlock(image, error, cacheType, url);
                }
            });
        }];

        // step4.2 保存 当前下载所对应的operation(目前operation仅支持cancel 接口)
        [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];
    } else { // 如果图片url 为nil,则直接返回错误
        dispatch_main_async_safe(^{
            [self removeActivityIndicator];
            if (completedBlock) {
                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
                completedBlock(nil, error, SDImageCacheTypeNone, url);
            }
        });
    }
}

代码通过注释,也轻松通晓。SDWebImage加载图片,分为如下多少个步骤:

  1. 先关闭在此以前的下载
  2. 将近日下载图片的url作为UIImageView的品质(利用runtime完结)
  3. 假使有placeholder,则直接设置当前image为placeholder
  4. 调用SDWebImageManager单实例的downloadImageWithUSportageL方法,加载图片(这里说加载并不是下载,是因为最后图片来自或者是缓存),并赶回叁个id<SDWebImageOperation>对象(其实是三个SDWebImageCombinedOperation对象),代表当前的加载图片操作。
  5. 将id<SDWebImageOperation>对象保存,当需求撤废加载时,仅须求调用id<SDWebImageOperation>公约章程cancel。
  6. 在SDWebImageManger的downloadImageWithU瑞虎L方法的回调block中,借使加载image图片成功,则设置UIImageIView的image属性,并调用completedBlock,若退步,则在completedBlock中回到失利error。
    上述就是通过SDWebImage加载图片的漫天流程,到这几天停止,流程很明白。
    大家能够开掘,这里的主干逻辑,就在于调用了SDWebImageManger的downloadImageWithUGL450L方法。那么,大家接下去看看SDWebImageManger的代码。

规划指标

SDWebImage
是我们常常使用的多个异步图片加载库,在项目中动用SDWebImage来管理图片加载相关操作能够不小地进步开辟功能,让我们进一步专一于专门的工作逻辑完结。

  • 从缓存中找出图片
  • 图表存在,显示图片
  • 图片荒诞不经,下载图片
  • 下载完毕后,将图纸加多到缓存,显示图片

SDWebImageManager

先看SDWebImageManager的头文件:

/**
 * The SDWebImageManager is the class behind the UIImageView+WebCache category and likes.
 * It ties the asynchronous downloader (SDWebImageDownloader) with the image cache store (SDImageCache).
 * You can use this class directly to benefit from web image downloading with caching in another context than
 * a UIView.
 *
 * Here is a simple example of how to use SDWebImageManager:
 *
 * @code

SDWebImageManager *manager = [SDWebImageManager sharedManager];
[manager downloadImageWithURL:imageURL
                      options:0
                     progress:nil
                    completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                        if (image) {
                            // do something with image
                        }
                    }];

 * @endcode
 */
@interface SDWebImageManager : NSObject

@property (weak, nonatomic) id <SDWebImageManagerDelegate> delegate;

@property (strong, nonatomic, readonly) SDImageCache *imageCache;
@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader;

/**
 * The cache filter is a block used each time SDWebImageManager need to convert an URL into a cache key. This can
 * be used to remove dynamic part of an image URL.
 *
 * The following example sets a filter in the application delegate that will remove any query-string from the
 * URL before to use it as a cache key:
 *
 * @code

[[SDWebImageManager sharedManager] setCacheKeyFilter:^(NSURL *url) {
    url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
    return [url absoluteString];
}];

 * @endcode
 */
@property (nonatomic, copy) SDWebImageCacheKeyFilterBlock cacheKeyFilter;

/**
 * Returns global SDWebImageManager instance.
 *
 * @return SDWebImageManager shared instance
 */
+ (SDWebImageManager *)sharedManager;

/**
 * Allows to specify instance of cache and image downloader used with image manager.
 * @return new instance of `SDWebImageManager` with specified cache and downloader.
 */
- (instancetype)initWithCache:(SDImageCache *)cache downloader:(SDWebImageDownloader *)downloader;

/**
 * Downloads the image at the given URL if not present in cache or return the cached version otherwise.
 *
 * @param url            The URL to the image
 * @param options        A mask to specify options to use for this request
 * @param progressBlock  A block called while image is downloading
 * @param completedBlock A block called when operation has been completed.
 *
 *   This parameter is required.
 * 
 *   This block has no return value and takes the requested UIImage as first parameter.
 *   In case of error the image parameter is nil and the second parameter may contain an NSError.
 *
 *   The third parameter is an `SDImageCacheType` enum indicating if the image was retrieved from the local cache
 *   or from the memory cache or from the network.
 *
 *   The last parameter is set to NO when the SDWebImageProgressiveDownload option is used and the image is 
 *   downloading. This block is thus called repeatedly with a partial image. When image is fully downloaded, the
 *   block is called a last time with the full image and the last parameter set to YES.
 *
 * @return Returns an NSObject conforming to SDWebImageOperation. Should be an instance of SDWebImageDownloaderOperation
 */
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;

/**
 * Saves image to cache for given URL
 *
 * @param image The image to cache
 * @param url   The URL to the image
 *
 */

- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url;

/**
 * Cancel all current operations
 */
- (void)cancelAll;

/**
 * Check one or more operations running
 */
- (BOOL)isRunning;

/**
 *  Check if image has already been cached
 *
 *  @param url image url
 *
 *  @return if the image was already cached
 */
- (BOOL)cachedImageExistsForURL:(NSURL *)url;

/**
 *  Check if image has already been cached on disk only
 *
 *  @param url image url
 *
 *  @return if the image was already cached (disk only)
 */
- (BOOL)diskImageExistsForURL:(NSURL *)url;

/**
 *  Async check if image has already been cached
 *
 *  @param url              image url
 *  @param completionBlock  the block to be executed when the check is finished
 *  
 *  @note the completion block is always executed on the main queue
 */
- (void)cachedImageExistsForURL:(NSURL *)url
                     completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;

/**
 *  Async check if image has already been cached on disk only
 *
 *  @param url              image url
 *  @param completionBlock  the block to be executed when the check is finished
 *
 *  @note the completion block is always executed on the main queue
 */
- (void)diskImageExistsForURL:(NSURL *)url
                   completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;


/**
 *Return the cache key for a given URL
 */
- (NSString *)cacheKeyForURL:(NSURL *)url;

@end

批注相当多,其实真正的代码并非常的少。注意到起来的一段注释(结合本身知道简单翻译一下):

SDWebImageManager为UIImageView+WebCache加载图片提供了主旨完毕。它实际是管理了图片异步的下载以及缓存管理,同一时常间,大家能够直接行使SDWebImageManager来加载网络图片,而不光是将其用来UIImageView中。所以,未来我们想下载网络图片,而不独有是为了做UI显示时,能够一贯采纳SDWebImageManager来下载图片以及缓存管理。

在这里,我们照旧先关切图片是怎样加载的,除掉其他无关的代码,SDWebImageManager能够简化为:

@interface SDWebImageManager : NSObject

@property (weak, nonatomic) id <SDWebImageManagerDelegate> delegate;

@property (strong, nonatomic, readonly) SDImageCache *imageCache;  // 处理缓存相关
@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader;   // 处理图片下载
@property (nonatomic, copy) SDWebImageCacheKeyFilterBlock cacheKeyFilter;  // 用于过滤处理图片的url(这里可先不关注)
// 表明我是一个单例
+ (SDWebImageManager *)sharedManager;  

- (instancetype)initWithCache:(SDImageCache *)cache downloader:(SDWebImageDownloader *)downloader;

// 核心加载图片方法 downloadImageWithURL
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;

SDWebImageManager的运用downloadImageWithU路虎极光L方法加载图片。大家就来分析它的源代码:

- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {
    // 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.
    if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    }

    // Prevents app crashing on argument type error like sending NSNull instead of NSURL
    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }

    __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    __weak SDWebImageCombinedOperation *weakOperation = operation;

    // 如果是曾经失败的url 且当前的options不需重新尝试失败的链接,则直接返回错误
    BOOL isFailedUrl = NO;
    @synchronized (self.failedURLs) {
        isFailedUrl = [self.failedURLs containsObject:url];
    }
    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;
    }

    @synchronized (self.runningOperations) {
        [self.runningOperations addObject:operation];
    }
    NSString *key = [self cacheKeyForURL:url];

    // step1. 先读取缓存
    operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {

        // 读取缓存结束(可能有缓存, 也可能没有)
        /////////////////////////////////////////////

        if (operation.isCancelled) {
            @synchronized (self.runningOperations) {
                [self.runningOperations removeObject:operation];
            }

            return;
        }

        //// 缓存没有或必须刷新缓存等, 走network download路线
        ///////////////////////////////////////
        if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
            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
            /////////////////////////////////////////////////////////////////////
            // step1.  将外部传入的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 (image && options & SDWebImageRefreshCached) {  // 特殊处理一下 SDWebImageRefreshCached 请求的options
                // 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;
            }

            // step2. 利用SDWebImageDownloader开始下载 并返回一个 id <SDWebImageOperation>subOperation 代表当前下载operation(这里利用operation的cancel block对subOperaton 进行retain)
            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
                }
                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) {
                        @synchronized (self.failedURLs) {
                            [self.failedURLs addObject:url];
                        }
                    }
                }
                else {
                    if ((options & SDWebImageRetryFailed)) {
                        @synchronized (self.failedURLs) {
                            [self.failedURLs removeObject:url];
                        }
                    }

                    // 下载成功,做cache
                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);

                    if (options & SDWebImageRefreshCached && image && !downloadedImage) {  // 如果download是因为refresh cache引起,而又没有真正的download image下来,什么也不做
                        // 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];
                            }

                            dispatch_main_sync_safe(^{
                                if (strongOperation && !strongOperation.isCancelled) {
                                    completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
                                }
                            });
                        });
                    }
                    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);
                            }
                        });
                    }
                }
                // 如果结束,则删除该SDWebImageCombinedOperation
                if (finished) {
                    @synchronized (self.runningOperations) {
                        if (strongOperation) {
                            [self.runningOperations removeObject:strongOperation];
                        }
                    }
                }
            }];

            // step3. 设置operation的cancel block
            operation.cancelBlock = ^{
                [subOperation cancel];

                @synchronized (self.runningOperations) {

                    __strong __typeof(weakOperation) strongOperation = weakOperation;
                    if (strongOperation) {
                        [self.runningOperations removeObject:strongOperation];
                    }
                }
            };
        }
        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;
}

代码的逻辑大约分为三局地:

  1. 先决断image的U景逸SUVL是或不是合法
    2.若UCR-VL合法,则先读取缓存
    3.若缓存尚无该文件,则在网络上下载

透过注释,应该也轻便精通。
略过检查U奥迪Q7L合法性的代码,大家率先来看SDWebImage是何许读取Cache的。
也就是
SDImageCache 类
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;
方法。

特性

SDWebImage的完结流程

澳门葡京备用网址 2

SDWebimage.png

在这一个类中,将图纸缓存功用与上一章所说的图片下载功用关联到了一块儿,构成了SDWebImage的主体图片展示逻辑。

SDImageCache

大家先看queryDiskCacheForKey的方法注脚:

typedef NS_ENUM(NSInteger, SDImageCacheType) {
    /**
     * The image wasn't available the SDWebImage caches, but was downloaded from the web.
     */
    SDImageCacheTypeNone = 1,
    /**
     * The image was obtained from the disk cache.
     */
    SDImageCacheTypeDisk,
    /**
     * The image was obtained from the memory cache.
     */
    SDImageCacheTypeMemory
};

typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType cacheType);
/**
 * Query the disk cache asynchronously.
 *
 * @param key The unique key used to store the wanted image
 */
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;
  1. queryDiskCacheForKey的再次来到值为三个NSOperation对象,外界能够通过该对象撤消cache的探求进程。
    注意,这里SDWebImage数十次使用了该思路(加载Image,下载Image),通过同步的归来一个表示当前操作的对象,来对异步操作举办田间处理。
  2. 参数接受二个U瑞鹰L MD5值作为Key查找Cache。
  3. 在回调block中,再次来到UIImage,并用SDImageCacheType来注解缓存文件的发源:未有,内部存款和储蓄器,磁盘。
    此间,我们也可以识破SDWebImage的cache攻略,先读内部存款和储蓄器,未有打中,才去读磁盘。

我们再来看她的具体贯彻

- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
    if (!doneBlock) {
        return nil;
    }

    if (!key) {
        doneBlock(nil, SDImageCacheTypeNone);
        return nil;
    }

    // First check the in-memory cache...
    UIImage *image = [self imageFromMemoryCacheForKey:key];  // NSCache is threadsafe, don't need lock
    if (image) {
        doneBlock(image, SDImageCacheTypeMemory);
        return nil;
    }

    // Second check disk cache...
    NSOperation *operation = [NSOperation new]; // 这个NSOperation 对象的意义在于向外提供了cancel读取disk cache的对象
    dispatch_async(self.ioQueue, ^{ // aync call. Note there sync io operation by serial queue
        if (operation.isCancelled) {
            return;
        }

        @autoreleasepool {
            UIImage *diskImage = [self diskImageForKey:key];
            if (diskImage && self.shouldCacheImagesInMemory) {
                NSUInteger cost = SDCacheCostForImage(diskImage);
                [self.memCache setObject:diskImage forKey:key cost:cost];
            }

            dispatch_async(dispatch_get_main_queue(), ^{  // return to main queue
                doneBlock(diskImage, SDImageCacheTypeDisk);
            });
        }
    });

    return operation;
}
  1. First check in-memory cahce.
    此处的memory
    cahce用了一连自系统的NSCache的AutoPurgeCache对象。该指标在init时会注册监听系统的
    UIApplicationDidReceiveMemoryWarningNotification 通知。
    当系统内部存款和储蓄器告警时,会活动清除全体的memory cache:

 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
  1. Second check disk cache。
    此间注意到,以前都是联合签字的回来nil,而在读取disk缓存时,才回到八个operation对象。那是因为事先的读取内部存款和储蓄器cache都以共同的,而这里disk读取Cache时异步的,为了为异步操作做管理,才须要想外重回叁个operation对象以便随时能够cancel该异步操作。
    在读取disk cache时,为了防守多线程同一时间读写二个文书,这里运用了
    串行队列
    self.ioQueue
    来串行化io操作。这是大家能够借鉴的地方,动用串行队列而非lock来担保线程安全

  2. 当从disk读取缓存后,再将disk缓存存入memory cache。

  3. 调用done block,将UIImage的cache(尽管部分话),以及Cache来源重回。

SDWebImage 与任何框架的争执统一

SDWebimage目录结构

澳门葡京备用网址 3

目录结构.png

图表缓存的代码主体类是SDImagaCache,无可争辩该类的入眼代码逻辑正是增、删、查、改等操作。这里大家重要来看上面那多少个关键点:

SDWebImage的Cache策略

那正是说,SDWebImage通过SDWebCache读取Cache的流水生产线如下:

澳门葡京备用网址 4

一体化框架结构,图片缓存。在下一篇文章中,作者会接着和大家共同分析,当Cache未命中时,SDWebImage是什么在网络下载图片,并更新Cahce的。

普及难题

贯彻原理

SDWebImage 是由四个 SDImageCache(多个管理缓存的类) 和
SDWebImageDownloader(担当下载网络图片) ,而
SDWebImageManager则是管事人将前两个结合起来实现整个工作流程。

1 、入口 setImageWithURL:placeholderImage:options: 会先把
placeholderImage显示,然后SDWebImageManager 依据 UENCOREL 开头拍卖图片。

2、进入
SDWebImageManagerdownloadWithURL:delegate:options:userInfo:,交给
SDImageCache 从缓存查找图片是不是曾经下载
queryDiskCacheForKey:delegate:userInfo:.

3、先从内存图片缓存查找是还是不是有图片,如若内部存款和储蓄器中已经有图表缓存,SDImageCacheDelegate回调
imageCache:didFindImage:forKey:userInfo 到 SDWebImageManager。

4、SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage:
到 UIImageView+WebCache 等前端显示图片。

5、固然内存缓存中并未有,生成 NSInvocationOperation
加多到行列开端从硬盘查找图片是不是曾经缓存。

6、依据 U君越LKey 在硬盘缓存目录下品尝读取图片文件。这一步是在 NSOperation
举办的操作,所以回主线程举办结果回调 notifyDelegate:

7、就算上一操作从硬盘读取到了图片,将图纸增加到内部存款和储蓄器缓存中(假使空闲内部存款和储蓄器过小,会先清空内部存款和储蓄器缓存)。SDImageCacheDelegate
回调 imageCache:didFindImage:forKey:userInfo:。进而回调展示图片。

8、假若从硬盘缓存目录读取不到图片,表明全体缓存都不设有该图形,须求下载图片,回调
imageCache:didNotFindImageForKey:userInfo:

9、分享或再度生成七个下载器 SDWebImageDownloader
起始下载图片。图片下载由 NSU奇骏LConnection 来做,达成相关 delegate
来决断图片下载中、下载落成和下载战败。

10、connection:didReceiveData: 中利用ImageIO
做了按图片下载进度加载效果。

connectionDidFinishLoading: 数据下载完成后提交 SDWebImageDecoder
做图片解码管理。

11、图片解码管理在四个 NSOperationQueue 完毕,不会拖慢主线程
UI。借使有亟待对下载的图形举办三遍拍卖,最棒也在此处达成,功能会好过多。

在主线程 notifyDelegateOnMainThreadWithInfo:
公布解码达成,imageDecoder:didFinishDecodingImage:userInfo: 回调给
SDWebImageDownloader

imageDownloader:didFinishWithImage:回调给
SDWebImageManager告知图片下载完结。

文告全部的downloadDelegates下载完毕,回调给急需的地点显得图片。

12、将图纸保存到 SDImageCache
中,内部存款和储蓄器缓存和硬盘缓存同临时候保留。写文件到硬盘也在以单独NSInvocationOperation完了,防止拖慢主线程。

SDImageCache
在初始化的时候会登记一些消息通告,在内存警告或退到后台的时候清理内部存储器图片缓存,应用结束的时候清理超时图片。

SDWebImage 也提供了 UIButton+WebCache 和
MKAnnotationView+WebCache,方便使用。

SDWebImagePrefetcher 可以事先下载图片,方便后续使用。

缓存图片首先要减轻的难题不怕通过什么样来区分一张张的图纸,大家放任自流的会想到图片的UOdysseyL链接。是的,SDWebImage也是这么干的:

用法

SDWebImageManager管理类

SDWebImageManager管理类中一同有十三个枚举方法,3个回调的block,2个代理方法,8个成员方法
拾二个枚举方法

typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {

    /**
     * 默认情况下,如果一个url在下载的时候失败了,那么这个url会被加入黑名单,不会尝试再次下载。如果使用该参数,则该URL不会被添加到黑名单中。意味着会对下载失败的URL尝试重新下载。
     * 此标记取消黑名单
     */
    SDWebImageRetryFailed = 1 << 0, //失败后尝试重新下载

    /**
     * 默认情况下,在 UI 交互时也会启动图像下载,此标记取消这一特性
     * 会推迟到滚动视图停止滚动之后再继续下载
     * 备注:NSURLConnection 的网络下载事件监听的运行循环模式是 NSDefaultRunLoopMode
     */
    SDWebImageLowPriority = 1 << 1, //低优先级

    SDWebImageCacheMemoryOnly = 1 << 2, //只使用内存缓存

    SDWebImageProgressiveDownload = 1 << 3, //渐进式下载

    /**
     * 遵守 HTPP 响应的缓存控制,如果需要,从远程刷新图像
     * 磁盘缓存将由 NSURLCache 处理,而不是 SDWebImage,这会对性能有轻微的影响
     * 此选项用于处理URL指向图片发生变化的情况
     * 如果缓存的图像被刷新,会调用一次 completion block,并传递最终的图像
     */
    SDWebImageRefreshCached = 1 << 4,   //刷新缓存


    SDWebImageContinueInBackground = 1 << 5,    //后台下载

    SDWebImageHandleCookies = 1 << 6,   //处理保存在NSHTTPCookieStore中的cookies


    SDWebImageAllowInvalidSSLCertificates = 1 << 7,     //允许不信任的 SSL 证书

    /**
     *  默认情况下,图像会按照添加到队列中的顺序被加载,此标记会将它们移动到队列前端被立即加载
     *  而不是等待当前队列被加载,因为等待队列加载会需要一段时间
     */
    SDWebImageHighPriority = 1 << 8,    //高优先级(优先下载)

    /**
     * 默认情况下,在加载图像时,占位图像已经会被加载。
     * 此标记会延迟加载占位图像,直到图像已经完成加载
     */
    SDWebImageDelayPlaceholder = 1 << 9,    //延迟占位图片

    SDWebImageTransformAnimatedImage = 1 << 10, //转换动画图像

    SDWebImageAvoidAutoSetImage = 1 << 11   //手动设置图像
};

3个block回调

//定义任务完成的block块
typedef void(^SDWebImageCompletionBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL);
//定义任务结束的block块
typedef void(^SDWebImageCompletionWithFinishedBlock)(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL);
//定义缓存过滤器的block块
typedef NSString *(^SDWebImageCacheKeyFilterBlock)(NSURL *url);

2个代理

/**
 * 如果该图片没有缓存,那么下载
 *
 * @param imageManager:当前的SDWebImageManager
 * @param imageURL:要下载图片的URL地址
 *
 * @return 如果要下载的图片在缓存中不存在,则返回NO,否则返回YES
 */
- (BOOL)imageManager:(SDWebImageManager *)imageManager shouldDownloadImageForURL:(NSURL *)imageURL;
/**

 * 允许在下载后立即将图像转换,并进行磁盘和内存缓存。
 *
 * @param imageManager 当前的SDWebImageManager
 * @param image 要转换你的图片
 * @param imageURL 要转换的图片的URL地址
 *
 * @return 变换后的图片对象
 */
- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL;

成员方法
该方法是管理类中最重要的方法

 /* 如果URL对应的图像在缓存中不存在,那么就下载指定的图片 ,否则返回缓存的图像
 *
 * @param url 图片的URL地址
 * @param options 指定此次请求策略的选项
 * @param progressBlock 图片下载进度的回调
 * @param completedBlock 操作完成后的回调
 *      此参数是必须的,此block没有返回值
 *      Image:请求的 UIImage,如果出现错误,image参数是nil
 *      error:如果出现错误,则error有值
 *      cacheType:`SDImageCacheType` 枚举,标示该图像的加载方式
 *          SDImageCacheTypeNone:从网络下载
 *          SDImageCacheTypeDisk:从本地缓存加载
 *          SDImageCacheTypeMemory:从内存缓存加载
 *          finished:如果图像下载完成则为YES,如果使用 SDWebImageProgressiveDownload 选项,同时只获取到部分图片时,返回 NO
 *          imageURL:图片的URL地址
 *
 * @return SDWebImageOperation对象,应该是SDWebimageDownloaderOperation实例
 */
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                                         options:(SDWebImageOptions)options
                                        progress:(SDWebImageDownloaderProgressBlock)progressBloc
                                       completed:(SDWebImageCompletionWithFinishedBlock)completedBlock;

措施解释

- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                     options:(SDWebImageOptions)options
                                    progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                   completed:(nullable SDInternalCompletionBlock)completedBlock {
    // 没有completedblock,那么调用这个方法是毫无意义的
    NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");

     //检查用户传入的URL是否正确,如果该URL是NSString类型的,那么尝试转换
    if ([url isKindOfClass:NSString.class]) {
        url = [NSURL URLWithString:(NSString *)url];
    }

    //防止因参数类型错误而导致应用程序崩溃,判断URL是否是NSURL类型的,如果不是则直接设置为nil
    if (![url isKindOfClass:NSURL.class]) {
        url = nil;
    }

    //初始化一个SDWebImageCombinedOperationBlock块
    __block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
    __weak SDWebImageCombinedOperation *weakOperation = operation;

    BOOL isFailedUrl = NO;
    if (url) {
        @synchronized (self.failedURLs) {
            isFailedUrl = [self.failedURLs containsObject:url];
        }
    }
    //如果url不正确或者 选择的下载策略不是『下载失败尝试重新下载』且该URL存在于黑名单中,那么直接返回,回调任务完成block块,传递错误信息
    if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
        [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
        return operation;
    }

    @synchronized (self.runningOperations) {
        [self.runningOperations addObject:operation];
    }

    //得到该URL对应的缓存KEY
    NSString *key = [self cacheKeyForURL:url];
    //该方法查找URLKEY对应的图片缓存是否存在,查找完毕之后把该图片(存在|不存在)和该图片的缓存方法以block的方式传递
    //缓存情况查找完毕之后,在block块中进行后续处理(如果该图片没有缓存·下载|如果缓存存在|如果用户设置了下载的缓存策略是刷新缓存如何处理等等)
    operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
        if (operation.isCancelled) {
            [self safelyRemoveOperationFromRunning:operation];
            return;
        }

        if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
            if (cachedImage && options & SDWebImageRefreshCached) {

                [self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
            }


            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;

            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) {
                    //如果是取消操作,就什么也不做
                } else if (error) {
                    [self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url: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 {
                    if ((options & SDWebImageRetryFailed)) {
                        @synchronized (self.failedURLs) {
                            [self.failedURLs removeObject:url];
                        }
                    }
                    //是否要进行磁盘缓存?
                    BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);

                    if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {

                    } else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
                        //否则,如果下载图片存在且(不是可动画图片数组||下载策略为SDWebImageTransformAnimatedImage&&transformDownloadedImage方法可用)
                        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 imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];
                            }
                            //在主线程中回调completedBlock
                            [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];
                }
            }];
            //取消操作
            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 {

            __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;
}

其他方法

/**
 * 根据图片的URL保存图片到缓存
 *
 * @param image:缓存的图片
 * @param url:该图片的URL地址
 */
- (void)saveImageToCache:(UIImage *)image forURL:(NSURL *)url;
/**
 * 取消当前所有操作
 */
- (void)cancelAll;
/**
 * 检查一个或多个操作是否正在运行
 */
- (BOOL)isRunning;
/*
 *  检查图像是否已经被缓存,如果已经缓存则返回YES
 *  @param url:图片对应的URL
 *
 *  @return 返回是否存在的BOOL值
 */
- (BOOL)cachedImageExistsForURL:(NSURL *)url;
/**
 *  检查图像是否存在磁盘缓存(此方法仅针对磁盘进行检查,只要存在就返回YES)
 *
 *  @param url 图片的url
 *
 *  @return 是否存在(只检查磁盘缓存)
 */
- (BOOL)diskImageExistsForURL:(NSURL *)url;
/**
 *  异步检查图像是否已经有内存缓存
 *
 *  @param URL             图片对应的URL
 *  @param completionBlock 当任务执行完毕之后调用的block
 *  @note  completionBlock始终在主队列执行
 */
- (void)cachedImageExistsForURL:(NSURL *)url
                     completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
/**
 *  异步检查图像是否已经有磁盘缓存
 *
 *  @param URL             图片对应的URL
 *  @param completionBlock 当任务执行完毕之后调用的block
 *  @note  completionBlock始终在主队列执行
 */
- (void)diskImageExistsForURL:(NSURL *)url
                   completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
/**
 * 返回指定URL的缓存键值,就是URL字符串
 */
- (NSString *)cacheKeyForURL:(NSURL *)url;

澳门葡京备用网址 5SDWebImageManager获得图片缓存使用的key澳门葡京备用网址 6SDImageCache保存图片到磁盘使用的名号

SDWebImage 4.0 迁移指南

SDWebImageDownloader下载类

SDWebImageDownloader的头文件内容很少,重如果概念了有个别基本参数如下载优先级战略、最大并发数、超时时间等

与大家怀想分歧的有个别是,它会到图片的U陆风X8L作MD5运算获得新的文书名,使用该名称来保存图片,防止UGL450L名称过长导致极度。

兑现原理

SDWebImageDownloader 中着力措施:下载图片的操作
/*
 * 使用给定的 URL 创建 SDWebImageDownloader 异步下载器实例
 * 图像下载完成或者出现错误时会通知代理
 */
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url
                            options:(SDWebImageDownloaderOptions)options
                       progress:(SDWebImageDownloaderProgressBlock)progressBlock                                                                                    
                    completed:(SDWebImageDownloaderCompletedBlock)completedBlock;

- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageDownloaderOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageDownloaderCompletedBlock)completedBlock {
    __block SDWebImageDownloaderOperation *operation;
    __weak __typeof(self)wself = self;  //为了避免block的循环引用
    //处理进度回调|完成回调等,如果该url在self.URLCallbacks并不存在,则调用createCallback block块
    [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^{

        //处理下载超时,如果没有设置过则初始化为15秒
        NSTimeInterval timeoutInterval = wself.downloadTimeout;
        if (timeoutInterval == 0.0) {
            timeoutInterval = 15.0;
        }
        // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
        //根据给定的URL和缓存策略创建可变的请求对象,设置请求超时
        //请求策略:如果是SDWebImageDownloaderUseNSURLCache则使用NSURLRequestUseProtocolCachePolicy,否则使用NSURLRequestReloadIgnoringLocalCacheData
        /*
         NSURLRequestUseProtocolCachePolicy:默认的缓存策略
            1)如果缓存不存在,直接从服务端获取。
            2)如果缓存存在,会根据response中的Cache-Control字段判断下一步操作,如: Cache-Control字段为must-revalidata, 则询问服务端该数据是否有更新,无更新的话直接返回给用户缓存数据,若已更新,则请求服务端.
         NSURLRequestReloadIgnoringLocalCacheData:忽略本地缓存数据,直接请求服务端。
         */
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];

        //设置是否使用Cookies(采用按位与)
        /*
         关于cookies参考:http://blog.csdn.net/chun799/article/details/17206907
         */
        request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
        //开启HTTP管道,这可以显著降低请求的加载时间,但是由于没有被服务器广泛支持,默认是禁用的
        request.HTTPShouldUsePipelining = YES;

        //设置请求头信息(过滤等)
        if (wself.headersFilter) {
            request.allHTTPHeaderFields = wself.headersFilter(url, [wself.HTTPHeaders copy]);
        }
        else {
            request.allHTTPHeaderFields = wself.HTTPHeaders;
        }

        //核心方法:创建下载图片的操作
        operation = [[wself.operationClass alloc] initWithRequest:request options:options progress:^(NSInteger receivedSize, NSInteger expectedSize) {
            SDWebImageDownloader *sself = wself;
            if (!sself) return;
            __block NSArray *callbacksForURL;
            dispatch_sync(sself.barrierQueue, ^{
                callbacksForURL = [sself.URLCallbacks[url] copy];
            });

            //遍历callbacksForURL数组中的所有字典,执行SDWebImageDownloaderProgressBlock回调
            for (NSDictionary *callbacks in callbacksForURL) {
                //说明:SDWebImageDownloaderProgressBlock作者可能考虑到用户拿到进度数据后会进行刷新处理,因此在主线程中处理了回调
                dispatch_async(dispatch_get_main_queue(), ^{
                    SDWebImageDownloaderProgressBlock callback = callbacks[kProgressCallbackKey];
                    if (callback) callback(receivedSize, expectedSize);
                });
            }
        } completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
            SDWebImageDownloader *sself = wself;
            if (!sself) return;
            __block NSArray *callbacksForURL;

            dispatch_barrier_sync(sself.barrierQueue, ^{
                callbacksForURL = [sself.URLCallbacks[url] copy];

                //如果完成,那么把URL从URLCallbacks字典中删除
                if (finished) {
                    [sself.URLCallbacks removeObjectForKey:url];
                }
            });

            //遍历callbacksForURL数组中的所有字典,执行SDWebImageDownloaderCompletedBlock回调
            for (NSDictionary *callbacks in callbacksForURL) {
                SDWebImageDownloaderCompletedBlock callback = callbacks[kCompletedCallbackKey];
                if (callback) callback(image, data, error, finished);
            }
        } cancelled:^{
            SDWebImageDownloader *sself = wself;
            if (!sself) return;

            //把当前的url从URLCallbacks字典中移除
            dispatch_barrier_async(sself.barrierQueue, ^{
                [sself.URLCallbacks removeObjectForKey:url];
            });
        }];
        //设置是否需要解码
        operation.shouldDecompressImages = wself.shouldDecompressImages;

        //身份认证
        if (wself.urlCredential) {
            operation.credential = wself.urlCredential;
        } else if (wself.username && wself.password) {
            //设置 https 访问时身份验证使用的凭据
            operation.credential = [NSURLCredential credentialWithUser:wself.username password:wself.password persistence:NSURLCredentialPersistenceForSession];
        }

        //判断下载策略是否是高优先级的或低优先级,以设置操作的队列优先级
        if (options & SDWebImageDownloaderHighPriority) {
            operation.queuePriority = NSOperationQueuePriorityHigh;
        } else if (options & SDWebImageDownloaderLowPriority) {
            operation.queuePriority = NSOperationQueuePriorityLow;
        }

        //把下载操作添加到下载队列中
        //该方法会调用operation内部的start方法开启图片的下载任务
        [wself.downloadQueue addOperation:operation];

        //判断任务的执行优先级,如果是后进先出,则调整任务的依赖关系,优先执行当前的(最后添加)任务
        if (wself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
            // Emulate LIFO execution order by systematically adding new operations as last operation's dependency
            [wself.lastAddedOperation addDependency:operation];

            wself.lastAddedOperation = operation;//设置当前下载操作为最后一个操作
        }
    }];
    return operation;
}

先从cache中寻觅图片,未有找到再品尝从磁盘中搜索。

架构图

SDImageCache缓存

SDWebImage完结了内部存款和储蓄器缓存和磁盘缓存,内部存款和储蓄器缓存是透过NSCache完成,磁盘缓存是因而NSFileManager来完结公文的蕴藏,磁盘缓存是异步完毕的。
主干的主意就是对图片缓存的增、删、查的实现

/**
 * 使用指定的命名空间实例化一个新的缓存存储
 * @param ns 缓存存储使用的命名空间
 */
- (id)initWithNamespace:(NSString *)ns;
/**
 * 使用指定的命名空间实例化一个新的缓存存储和目录
 * @param  ns        缓存存储使用的命名空间
 * @param  directory 缓存映像所在目录
 */
- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory;
//设置磁盘缓存路径
-(NSString *)makeDiskCachePath:(NSString*)fullNamespace;
/*
 * 如果希望在 bundle 中存储预加载的图像,可以添加一个只读的缓存路径
 * 让 SDImageCache 从 Bundle 中搜索预先缓存的图像
 * @param path 只读缓存路径(mainBundle中的全路径)
 */
- (void)addReadOnlyCachePath:(NSString *)path;
/**

 * 使用指定的键将图像保存到内存和磁盘缓存
 *
 * @param image 要保存的图片
 * @param key   唯一的图像缓存键,通常是图像的完整 URL
 */
- (void)storeImage:(UIImage *)image forKey:(NSString *)key;
/**
 * 使用指定的键将图像保存到内存和可选的磁盘缓存
 *
 * @param image  要保存的图片
 * @param key    唯一的图像缓存键,通常是图像的完整 URL
 * @param toDisk 如果是 YES,则将图像缓存到磁盘
 */
- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk;
/**
 * 使用指定的键将图像保存到内存和可选的磁盘缓存
 *
 * @param image       要保存的图像
 * @param recalculate 是否直接使用 imageData,还是从 UIImage 重新构造数据
 * @param imageData   从服务器返回图像的二进制数据,表示直接保存到磁盘
                      而不是将给定的图像对象转换成一个可存储/可压缩的图像格式,从而保留图片质量并降低 CPU 开销
 * @param key         唯一的图像缓存键,通常是图像的完整 URL
 * @param toDisk      如果是 YES,则将图像缓存到磁盘
 */
- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk;
/**
 * 异步查询磁盘缓存
 *
 * @param key         保存图像的唯一键
 * @param doneBlock   查询结束后的回调
 */
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;
/**
 * 同步查询内存缓存
 *
 * @param key  保存图像的唯一键
 */
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key;
/**
 * 查询内存缓存之后同步查询磁盘缓存
 *
 * @param key  保存图像的唯一键
 */
- (UIImage *)imageFromDiskCacheForKey:(NSString *)key;
/**
 * 同步从内存和磁盘缓存删除图像
 *
 * @param key  保存图像的唯一键
 */
- (void)removeImageForKey:(NSString *)key;
/**
 * 同步从内存和磁盘缓存删除图像
 *
 * @param key        保存图像的唯一键
 * @param completion 当图片被删除后会调用该block块
 */
- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion;
/**
 * 同步从内存和可选磁盘缓存删除图像
 *
 * @param key       保存图像的唯一键
 * @param fromDisk  如果是 YES,则从磁盘删除缓存
 */
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk;
/**
 * 同步从内存和可选磁盘缓存删除图像
 *
 * @param key         保存图像的唯一键
 * @param fromDisk    如果是 YES,则从磁盘删除缓存
 * @param completion  当图片被删除后会调用该block块
 */
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion;
/**
 * 删除所有内存缓存的图像
 */
- (void)clearMemory;
/**
 * 删除所有磁盘缓存的图像。
 * @param completion 删除操作后的块代码回调(可选)
 */
- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion;
/**
 * 删除所有磁盘缓存的图像
 * @see clearDiskOnCompletion:方法
 */
- (void)clearDisk;
/**
 * 从磁盘中删除所有过期的缓存图像。
 * @param completion 删除操作后的块代码回调(可选)
 */
- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock;
/**
 * 从磁盘中删除所有过期的缓存图像
 * @see cleanDiskWithCompletionBlock:方法
 */
- (void)cleanDisk;
/**
 * 获得磁盘缓存占用空间
 */
- (NSUInteger)getSize;
/**
 * 获得磁盘缓存图像的个数
 */
- (NSUInteger)getDiskCount;
/**
 * 异步计算磁盘缓存的大小
 */
- (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock;
/**
 *  异步检查图像是否已经在磁盘缓存中存在(不加载图像)
 *  @param key              保存图像的唯一键
 *  @param completionBlock  当图片被删除后会调用该block块
 *  @note  completionBlock总是在主线程
 */
- (void)diskImageExistsWithKey:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
/**
 *  检查图像是否已经在磁盘缓存中存在(不加载图像)
 *
 *  @param key 保存图像的唯一键
 *  @return 如果该图片存在,则返回YES
 */
- (BOOL)diskImageExistsWithKey:(NSString *)key;
/**
 * 获得指定 key 对应的缓存路径(需要指定缓存路径的根目录)
 *
 * @param key  键(可以调用cacheKeyForURL方法获得)
 * @param path 缓存路径根文件夹
 */
- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path;
/**
 *  获得指定 key 的默认缓存路径
 *
 *  @param key  键(可以调用cacheKeyForURL方法获得)
 *
 *  @return 默认缓存路径
 */
- (NSString *)defaultCachePathForKey:(NSString *)key;
UIImage* = [self imageFromMemoryCacheForKey:key];if{ doneBlock(image, SDImageCacheTypeMemory); return nil;}

流程图

UIImageView+WebCache方法入口

UIImageView+WebCache是SDWebImage方法的输入,当中最宗旨的方法是
享有安装图片调用的点子最后都会调用那些措施

- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletionBlock)completedBlock;

措施深入分析

//所有设置图片调用的方法最终都会调用这个方法
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock {
    [self sd_cancelCurrentImageLoad];//取消先前的下载任务
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);//动态添加属性

    if (!(options & SDWebImageDelayPlaceholder)) {
        //如果SDWebImageDelayPlaceholder为nil
        dispatch_main_async_safe(^{
            self.image = placeholder;//设置占位图
        });
    }
    //url不为nil
    if (url) {
        // check if activityView is enabled or not
        if ([self showActivityIndicatorView]) {
            //是否显示进度条
            [self addActivityIndicator];
        }
        //下载操作
        __weak __typeof(self)wself = self;
        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {

            [wself removeActivityIndicator];//移除进度条
            if (!wself) return;//self是否被释放
            dispatch_main_sync_safe(^{
                if (!wself) return;
                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock)
                {//不要自动设置图片,那么调用Block传入UIImage对象
                    completedBlock(image, error, cacheType, url);
                    return;
                }
                else if (image) {
                    //设置图片
                    wself.image = image;
                    [wself setNeedsLayout];
                } else {
                    if ((options & SDWebImageDelayPlaceholder)) {
                        //设置占位图片
                        wself.image = placeholder;
                        [wself setNeedsLayout];
                    }
                }
                if (completedBlock && finished) {
                    //调用Block
                    completedBlock(image, error, cacheType, url);
                }
            });
        }];
        //将生成的加载操作赋给UIView的自定义属性
        [self sd_setImageLoadOperation:operation forKey:@"UIImageViewImageLoad"];//给添加的属性赋值
    } else {
        dispatch_main_async_safe(^{
            //错误处理
            [self removeActivityIndicator];
            NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
            if (completedBlock) {
                completedBlock(nil, error, SDImageCacheTypeNone, url);
            }
        });
    }
}

从磁盘中搜寻时利用了二十四线程,由此全数查找方法的回来会为一个NSOperation对象。

目录结构

NSOperation* operation = [NSOperation new];dispatch_async(self.ioQueue, ^{ if(operation.isCancelled){ return; } @autoreleasepool{ UIImage* diskImage = [self diskImageForKey:key]; if(diskImage && self.shouldCacheImagesInMemory){ NSUInteger cost = SDCacheCostForImage(diskImage); [self.memCache setObject:diskImage forKey:key cost:cost]; } dispatch_async(dispatch_get_main_queue(), ^{ doneBlock(diskImage, SDImageCacheTypeDisk); }); }});return operation;

中央逻辑

此间要静心的一些是,ioQueue是叁个serial
queue,全数对磁盘缓存图片的增、删、改、查操作都被平放这几个行列中排队,保险了二十四线程操作的卓奥友峰。

金玉满堂细节

不是太喜欢一切工程里全部是Block的代码风格,那让自家觉获得恐慌。而在OC里面,一但二个环节选拔了Block,其后全部环节大概都亟待动用,且基本都是在前一环节Block的基本功上的越来越封装,很轻易就让阅读者陷入困境。有如何好的方案能立异这种境况吗?

  1. 图片下载

1.1 SDWebImageDownloader

1.2 SDWebImageDownloader

  1. 图表缓存——SDImageCache

  2. 图片加载管理器——SDWebImageManager

  3. 设置 UIImageView 的图片——UIImageView+WebCache

知识点

赢得与难题

启发与实践

延长阅读

一、简介
1. 企划目标
SDWebImage 提供了 UIImageView、UIButton 、MKAnnotationView
的图片下载分类,只要一行代码就能够完结图片异步下载和缓存效能。那样开荒者就不要花太多精力在图片下载细节上,潜心管理事务逻辑。
2. 特性
提供 UIImageView, UIButton, MKAnnotationView
的分类,用来呈现网络图片,以及缓存管理

异步下载图片

异步缓存(内部存款和储蓄器+磁盘),并且自动管理缓存有效性

后台图片解压缩

澳门葡京备用网址 ,同一个 U奥德赛L 不会再也下载

自动识别无效 URAV4L,不会每每重试

不打断主线程

高性能

使用 GCD 和 ARC

支撑多样图片格式(满含 WebP 格式)

支撑动图(GIF)

4.0 在此之前的动图效果并不是太好

4.0
现在依据FLAnimatedImage加载动图

注:本文选读的代码是 3.7.3 版本的,所以动图加载还不接济FLAnimatedImage。
3. SDWebImage 与其余框架的冲突统一
利润相关:以下两篇文章都以 SDWebImage
的拥护者所写,具备一定的主观性,仅供仿照效法。
How is SDWebImage better than
X?

iOS image caching. Libraries benchmark (SDWebImage vs
FastImageCache)

4. 大规模难点
标题 1:使用 UITableViewCell 中的 imageView
加载不一致尺寸的网络图片时会出现尺寸缩放难题

技术方案:自定义 UITableViewCell,重写 -layoutSubviews
方法,调度岗位尺寸;恐怕直接弃用 UITableViewCell 的
imageView,本人丰裕三个 imageView 作为子控件。
问题 2:图片刷新难题:SDWebImage 在进行缓存时马虎了具有服务器重临的
caching control 设置,並且在缓存时并未有做时间范围,那也就意味着图片 URAV4L
必需是静态的了,要求服务器上三个 U奥德赛L
对应的图形内容不容许更新。但是只要存款和储蓄图片的服务器不由本人调整,也便是说
图片内容更新了,U凯雷德L 却未曾立异,这种状态如何是好?

减轻方案:在调用 sd_setImageWithULacrosseL: placeholderImage:
options:方法时设置 options 参数为
SDWebImageRefreshCached,那样尽管会下降质量,不过下载图片时会照看到服务器重回的
caching control。
主题材料 3:在加载图片时,怎么着加多暗中认可的 progress indicator ?

技术方案:在调用 -sd_setImageWithU猎豹CS6L:方法从前,先调用下边包车型地铁格局:
[imageView sd_setShowActivityIndicatorView:YES];
[imageView sd_setIndicatorStyle:UIActivityIndicatorViewStyleGray];
5. 用法
5.1 UITableView 中使用 UIImageView+WebCache
UITabelViewCell 中的 UIImageView 控件直接调用 sd_setImageWithUXC60L:
placeholderImage:方法就能够
5.2 使用回调 blocks
在 block
中赢得图片下载进程和图纸加载成功(下载实现大概读取缓存)的回调,若是您在图纸加载成功前收回了央求操作,就不会接受成功或失利的回调

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:@
"[http://www.domain.com/path/to/image.jpg](http://www.domain.com/path/to/image.jpg)"
]


placeholderImage:[UIImage imageNamed:@
"placeholder.png"
]


completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {


... completion code here ...


}];

5.3 SDWebImageManager 的使用

UIImageView(WebCache) 分类的焦点在于 SDWebImageManager
的下载和缓存管理,SDWebImageManager将图片下载和图纸缓存组合起来了。SDWebImageManager也足以独立采纳。

SDWebImageManager *manager = [SDWebImageManager sharedManager];
    [manager loadImageWithURL:imageURL
                      options:0
                     progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                            // progression tracking code
                     }
                     completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                        if (image) {
                            // do something with image
                        }
                     }];

5.4 单独采用 SDWebImageDownloader 异步下载图片
咱俩还足以独立采用 SDWebImageDownloader
来下载图片,但是图片内容不会缓存。

 SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader];
    [downloader downloadImageWithURL:imageURL
                             options:0
                            progress:^(NSInteger receivedSize, NSInteger expectedSize) {
                                // progression tracking code
                            }
                           completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {
                                if (image && finished) {
                                    // do something with image
                                }
                            }];

5.5 单独使用 SDImageCache 异步缓存图片

SDImageCache 匡助内部存款和储蓄器缓存和异步的磁盘缓存(可选),假诺您想单独采用SDImageCache
来缓存数据的话,能够应用单例,也能够成立一个有独立命名空间的
SDImageCache 实例。

累加缓存的章程:
[[SDImageCache sharedImageCache] storeImage:myImage
forKey:myCacheKey];
私下认可情形下,图片数据会同期缓存到内部存款和储蓄器和磁盘中,要是您想只要内部存款和储蓄器缓存的话,能够应用下边的方法:
[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey
toDisk:NO];
读取缓存时能够行使 queryDiskCacheForKey:done: 方法,图片缓存的 key
是独一的,平常就是图表的 absolute UCR-VL。

SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"];
    [imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image) {
        // image is not nil if image was found
    }];

5.6 自定义缓存 key

突发性,一张图纸的 U君越L
中的一部分恐怕是动态变化的(譬如获取权力上的限量),所以我们只需求把 UKugaL
中不改变的一些作为缓存用的 key。

   SDWebImageManager.sharedManager.cacheKeyFilter = ^(NSURL *url) {
            url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];
            return [url absoluteString];
        };

6. SDWebImage 4.0
迁移指南

遵纪守法版本号惯例(塞马ntic Versioning),从版本号能够看到 SDWebImage 4.0
是一个大学本科子,在结构上和 API 方面都具备更改。
除了 iOS 和 tvOS 之外,SDWebImage 4.0 还接济越多的阳台——watchOS 和 马克斯OS X。
借助FLAnimatedImage在动图支持上做了考订,尤其是
GIF。

1. 索引结构
Downloader

SDWebImageDownloader

SDWebImageDownloaderOperation

Cache

SDImageCache

Utils

SDWebImageManager

SDWebImageDecoder

SDWebImagePrefetcher

Categories

UIView+WebCacheOperation

UIImageView+WebCache

UIImageView+HighlightedWebCache

UIButton+WebCache

MKAnnotationView+WebCache

NSData+ImageContentType

UIImage+GIF

UIImage+MultiFormat

UIImage+WebP

Other

SDWebImageOperation(协议)

SDWebImageCompat(宏定义、常量、通用函数)

澳门葡京备用网址 7

1494499913406062.png

  1. 核心逻辑

下载 Source code(3.7.3),运维 pod install,然后打开SDWebImage.xcworkspace,先 run 起来感受一下。

在理解细节从前大家先概览壹次主流程,也正是最主题的逻辑。

我们从 MasterViewController 中的 [cell.imageView
sd_setImageWithURL:url placeholderImage:placeholderImage]; 起先看起。

经过层层调用,直到 UIImageView+WebCache 中最中央的主意
sd_setImageWithU昂CoraL: placeholderImage: options: progress:
completed:。该方法中,首要做了以下几件事:

打消当前正值扩充的加载职务 operation
设置 placeholder
假如 UHavalL 不为 nil,就经过 SDWebImageManager 单例开启图片加载任务operation,SDWebImageManager 的图片加载方法中会再次回到一个SDWebImageCombinedOperation 对象,那个目的包罗三个 cacheOperation 和多少个cancelBlock。
SDWebImageManager 的图片加载方法
downloadImageWithU科雷傲L:options:progress:completed: 中会先拿图片缓存的 key
(那么些 key 暗中认可是图片 U奥德赛L)去 SDImageCache
单例中读取内部存款和储蓄器缓存,如若有,就重回给
SDWebImageManager;假若内部存款和储蓄器缓存未有,就敞开异步线程,拿经过 MD5 管理的
key
去读取磁盘缓存,借使找到磁盘缓存了,就伙同到内存缓存中去,然后再再次回到给
SDWebImageManager。

万一内部存款和储蓄器缓存和磁盘缓存中都未曾,SDWebImageManager 就能调用
SDWebImageDownloader 单例的 -downloadImageWithU昂科拉L: options: progress:
completed: 方法去下载,该会先将盛传的 progressBlock 和 completedBlock
保存起来,并在率先次下载该 UPAJEROL 的图纸时,成立叁个 NSMutableU大切诺基LRequest
对象和多个 SDWebImageDownloaderOperation 对象,并将该
SDWebImageDownloaderOperation 对象增添到 SDWebImageDownloader
的downloadQueue 来运营异步下载义务。

SDWebImageDownloaderOperation 中封装了三个 NSUENCORELConnection
的互联网乞求,并经过 runloop 来维持 NSU安德拉LConnection 在 start
后、收到响应前不被干掉,下载图片时,监听 NSU宝马7系LConnection 回调的
-connection:didReceiveData: 方法中会肩负 progress 相关的拍卖和回调,-
connectionDidFinishLoading: 方法中会肩负将 data 转为
image,以及图片解码操作,并最终回调 completedBlock。

SDWebImageDownloaderOperation 中的图片下载诉求达成后,会回调给
SDWebImageDownloader,然后 SDWebImageDownloader 再回调给
SDWebImageManager,SDWebImageManager
中再将图片分别缓存到内部存款和储蓄器和磁盘上(可选),并回调给
UIImageView,UIImageView 中再重临主线程设置 image
属性。至此,图片的下载和缓存操作就圆满甘休了。

自然,SDWebImage
中还会有众多细节能够深挖,包蕴一些神奇设计和知识点,接下去再看看SDWebImage
中的实现细节。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website