SDWebImage源码阅读1——整体系统结构。SDWebImage源码解析。

前言

SDWebImage作一个加载图片开源库,在大部类型里她和片刷新、弹窗控件一样普及。而且这事物写得格外好,网上曾出过多首对之库剖析的篇章了。正好这段时日发出时光,我耶花了众多龙时间认真读书了产她的代码。确实发受益颇多,因此尽管专门写首笔记,一来再次梳理下思路,加深理解。二来也是吗利日后习,温故而知新。

好了,开始。


SDWebImage库总体分为这么几单部分:

分析

自家好写了同样摆设图,展示了SDWebImage每当加载图片时的全体系统结构。先以此图显示出,下面边分析限回过头来对照该图加深理解。

屏幕快照 2016-08-01 下午2.31.38.png

咱俩一般以在路面临凡这样使她的:

    UIImage *image = [UIImage imageNamed:@"1.jpg"];
    NSString *urlStr = @"http://dn-edu-test.qbox.me/5436557847300726784";
    NSURL *url = [NSURL URLWithString:urlStr];

    [_imgV setImageWithURL:url placeholderImage:image];

SDWebImage供的接口方法非常简单,只待一个图形的url和一个占位图作为参数而已。而且它们的接口方法是盖UIImageView的分类的方提供的,而未经过连续的道,这样降低了耦合和侵入性,同时为暴跌了使用者的采用本,不用在创造UIImageView时不时继续某类,或使用其包裹好之某类。只要以类似吃导入了该分类的腔文件,就足以经过UIImageView的实例对象调用库里接口方法。


  1. 类似UIImageView+WebCache这么的面向使用者的接口,由各个分类的sd_setImageWithURL:...一系列措施结合,是咱们最常接触使用的有的。
  2. SDImageCache,是SDWebImage库中负责缓存工作之切近。
  3. SDImageDownloader,是SDWebImage仓库中负下载工作的接近。
  4. SDWebImageManager,是管理SDImageCacheSDImageDownloader,让其协同工作的类。在相继分类的暗中偷工作。

UIImageView+WebCache.m

咱打使用者的看法开始,从外向内一薄薄的分析其的代码。首先我们进摁住command键的而点击[_imgV setImageWithURL:url placeholderImage:image];,便上了UIImageView+WebCache.m分拣文件。

#import "UIImageView+WebCache.h"
#import "objc/runtime.h"

// 3个动态添加的属性
static char imageURLKey; // 图片url
static char operationKey;   // 操作
static char operationArrayKey; // 一组操作

@implementation UIImageView (WebCache)

- (void)setImageWithURL:(NSURL *)url {
    [self setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:nil];
}

- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder {
    [self setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:nil];
}

- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options {
    [self setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
}

- (void)setImageWithURL:(NSURL *)url completed:(SDWebImageCompletedBlock)completedBlock {
    [self setImageWithURL:url placeholderImage:nil options:0 progress:nil completed:completedBlock];
}

- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder completed:(SDWebImageCompletedBlock)completedBlock {
    [self setImageWithURL:url placeholderImage:placeholder options:0 progress:nil completed:completedBlock];
}

- (void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options completed:(SDWebImageCompletedBlock)completedBlock {
    [self setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:completedBlock];
}

随即段代码值得说的凡,所有暴露于外之接口方法中其实还是当调用一个参数最多之一个方,我们且将该名全能方法。这样的话将着力代码封装在平高居,不必写冗余饿的代码,且看起一目了然。
我们友好于描绘一个自定义控件或者说好写东西常常,提供的初始化方法以及职能方法极其好还盖这个种植样式写。
此外,该分类声明了三单静态字符,均用于通过运行时动态地叫分类上加属性,也给属性关联。分类中凡是勿得以一直定义属性之,需要经过运行时加上。

对接下去我们跳入全能方法观测她举行了哟。首先,我们看看该方法起五单参数,分别是图形url,占位图placeholder,配置选options,下充斥进行着之快慢block回调progressBlock,下载就后的completedBlock回调。

- (void)setImageWithURL:(NSURL *)url
       placeholderImage:(UIImage *)placeholder
                options:(SDWebImageOptions)options
               progress:(SDWebImageDownloaderProgressBlock)progressBlock
              completed:(SDWebImageCompletedBlock)completedBlock;

下一场,我们开始同步一步之观赛是办法中做了什么:

- (void)setImageWithURL:(NSURL *)url
       placeholderImage:(UIImage *)placeholder
                options:(SDWebImageOptions)options
               progress:(SDWebImageDownloaderProgressBlock)progressBlock
              completed:(SDWebImageCompletedBlock)completedBlock {
    [self cancelCurrentImageLoad]; // 取消当前的operation(取消操作并设置operationKey属性为nil)
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC); // 设置imageURLKey属性
    self.image = placeholder; // 先设置占位图

    if (!(options & SDWebImageDelayPlaceholder)) { // SDWebImageDelayPlaceholder枚举值的含义是取消网络图片加载好前展示占位图片。所以在这里并不能直接把placeholder直接赋值给self.image,而要用if条件排除这种情况。
        self.image = placeholder;
    }

    if (url) {
        // 获取图片(先从内存找,若无再从磁盘找,若仍无则从网络下载。)
        // 先从内存找,若找到,则在主线程设置图片,并调用block回调;若在磁盘找到,则还要将其添加到内存缓存再做后续的;若是从网络下载而得,则内存和磁盘均要添加缓存,而后再进行后续的事。
        __weak UIImageView *wself = self;
        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager downloadWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished) {
            if (!wself) return;
            dispatch_main_sync_safe(^{ // dispatch_main_sync_safe宏的目的是确保一定在主线程
                if (!wself) return;
                if (image) {
                    wself.image = image;
                    [wself setNeedsLayout];
                } else {
                    if ((options & SDWebImageDelayPlaceholder)) { // 这个条件在这里又出现了,SDWebImageDelayPlaceholder的含义是网络图片加载前不显示占位图片,但此时网络图片没有成功请求到,所以此时需要加载占位图。
                        wself.image = placeholder;
                        [wself setNeedsLayout];
                    }
                }
                if (completedBlock && finished) {
                    completedBlock(image, error, cacheType);
                }
            });
        }];
        objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC); // 获取图片的同时,将该操作赋为UIImageView+WebCache的属性。
    }
}

拖欠方式中一直达来第一就是调用了点子[self cancelCurrentImageLoad];注销了即UIImageView对象的取得图片操作;然后用参数url设为该分类的一个性;接着设置了UIImageView对象的占有个图;然后就调用了SDWebImageManager的实例对象方法获得图片,在该扭曲调block里,若得图片成功,则以那个设为UIImageView的图形,若得失败则用placeholder给为占用个图,并且最后以不负众望的block回调。(尽管我们一般采用的凡从未有过回调的接口,若改也有回调的接口,下载就后是好接到回调的)
SDWebImageManager的取图片的对象方法是异步下充斥图片的,该办法return的是id <SDWebImageOperation>类的靶子,它象征一个兑现了SDWebImageOperation说道的别样对象,在此处代表获取图片这一操作。在调用了该办法取得图片的后,旋即我们就拿拖欠措施的返值,即该operation加之为该分类的性。至于为何要拿其予以为性?因为它们意味着获取图片的“操作”,我们要允许继续将获取图片的操作取消。(下面马上会讲解“取消时拿走图片的操作”的点子)
objc_setAssociatedObject(self, &operationKey, operation, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

点的代码来几地处按照要细说,或者说小心一下。
First:无异于开始调用的撤销时获取图片的操作十分方式中是如此实现的:

// 取消当前UIImageView对象的获取图片操作,并将分类的该属性赋为nil
- (void)cancelCurrentImageLoad {
    // Cancel in progress downloader from queue
    id <SDWebImageOperation> operation = objc_getAssociatedObject(self, &operationKey);
    if (operation) {
        [operation cancel];
        objc_setAssociatedObject(self, &operationKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
}

即由此运行时get到眼前操作的特性对象operation,然后它调用了[operation cancel];道,其实是cancel措施是这么回事:获取图片的办法返回的是“获取图片的操作”——operation对象,这个operation凡是实现了SDWebImageOperation磋商的一个靶。其实以获图片方法中我们就可以看到(后面将会说交)该目标是类似SDWebImageCombinedOperation创立的,它正是落实了SDWebImageOperation商,并促成了cancel主意,在中开展了更多的吊销处理(不仅是调用了NSOperationcancel方法)。

- (void)cancel {
    self.cancelled = YES;
    if (self.cacheOperation) {
        [self.cacheOperation cancel];
        self.cacheOperation = nil;
    }
    if (self.cancelBlock) {
        self.cancelBlock();
        self.cancelBlock = nil;
    }
}

**
Second:**SDWebImageDelayPlaceholder斯枚举值的意思是吊销网络图片加载好前展示占位图片,即网络图片加载好前未亮占各类图。

if (!(options & SDWebImageDelayPlaceholder)) { 
        self.image = placeholder;
    }

每当此并无能够直接把placeholder直接赋值给self.image,而设因此if条件排除这种情景。然而在网络下充斥图片的回调里,若无博得图片的景下同时发生段子这样的代码:

if ((options & SDWebImageDelayPlaceholder)) { 
                        wself.image = placeholder;
                        [wself setNeedsLayout];
                    }

此刻网图片并未成功请求到,而占据位图此前又没设置,所以此时只有就这种情况要加载占个图。
**
Third:**于得图片的block回调里我们好看看,代码中采取的是dispatch_main_sync_safe,它是受定义之一个宏大,目的是为了确保是以主线程的。因为安图片这些UI操作,必须得在主线程进行。

#define dispatch_main_sync_safe(block)\
    if ([NSThread isMainThread]) {\
        block();\
    }\
    else {\
        dispatch_sync(dispatch_get_main_queue(), block);\
    }

UIView+WebCacheOperation

夫分类中提供了几乎单法子,可以对operation进行操作。

先是这个分类中,使用RuntimeUIView累加一个字典属性(NSMutableDictionary<NSString *, id>),里面储存了片下载操作id <SDWebImageOperation> operation,可能也一个op或者op集合。

SDWebImageManager

接下来我们点击downloadWithURL:options: progress: completed:主意就跳入了SDWebImageManager类吃。该类是包了获取图片的浑动作。我们事先来探望SDWebImageManager.h头文件之代码吧。

@interface SDWebImageManager : NSObject

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

@property (strong, nonatomic, readonly) SDImageCache *imageCache; // 关于缓存
@property (strong, nonatomic, readonly) SDWebImageDownloader *imageDownloader; // 关于下载
@property (strong) NSString *(^cacheKeyFilter)(NSURL *url);


+ (SDWebImageManager *)sharedManager;

- (id <SDWebImageOperation>)downloadWithURL:(NSURL *)url
                                    options:(SDWebImageOptions)options
                                   progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                  completed:(SDWebImageCompletedWithFinishedBlock)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 opreations
 */
- (void)cancelAll;

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

/**
 * Check if image has already been cached
 */
- (BOOL)cachedImageExistsForURL:(NSURL *)url;
- (BOOL)diskImageExistsForURL:(NSURL *)url;

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

@end

得视属性被产生个别独凡是坏关键之:imageCacheimageDownloader个别表示针对缓存的处理与指向下载的拍卖。同样下面为定义好几独方法,都自带了诠释。
紧接下我们要看downloadWithURL:options: progress: completed:术的实现。代码太长就非举列有了,只排有关键代码:

- (id <SDWebImageOperation>)downloadWithURL:(NSURL *)url
                                    options:(SDWebImageOptions)options
                                   progress:(SDWebImageDownloaderProgressBlock)progressBlock
                                  completed:(SDWebImageCompletedWithFinishedBlock)completedBlock;

    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]; // 该方法return的就是该operation实例对象
    __weak SDWebImageCombinedOperation *weakOperation = operation;

    BOOL isFailedUrl = NO;
    @synchronized (self.failedURLs) {
        isFailedUrl = [self.failedURLs containsObject:url]; // 判断该url是否已在“黑名单”内
    }

    // 对于url为空或在黑名单内且不允许再次请求配置的情况,则直接在此时用block回调,并抛出错误,而不用费力做后续步骤。
    if (!url || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
        dispatch_main_sync_safe(^{
            NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil];
            completedBlock(nil, error, SDImageCacheTypeNone, YES);
        });
        return operation;
    }

    @synchronized (self.runningOperations) {
        [self.runningOperations addObject:operation]; // 将当前操作,添加进runningOperations数组,表示当前正在进行的操作们
    }
    NSString *key = [self cacheKeyForURL:url]; // 转换为url的字符串,图片在缓存中是以url字符串为key存储的。
    // 先从缓存中查找。返回NSOperation类型的operation对象
    operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) {
        if (operation.isCancelled) {
            @synchronized (self.runningOperations) {
                [self.runningOperations removeObject:operation];
            }

            return;
        }

        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 bug 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); // 有图片就通过block回调。
                });
            }
  ...
  ...

先是一开始针对参数的校验及处理,我们温馨写代码也必要养成这样严谨的代码习惯。在章程中首先对参数进行非空或者其它组成部分校验处理,一来可防止因参数值为nil引起的夭折,二来若参数为nil,则大部分情景下得以直接return了,不必还难往生实施了。同样的道理,下面判断了该url是否以黑名单内,若以黑名单内,并且是“不允许再次要”配置的景象下,就直以这所以block回调,并丢掉来左,而未用费力执行后续动作。

下一场我们看出通过SDWebImageCombinedOperation好像创建了operation对象,该方法return的虽是该目标,SDWebImageCombinedOperation接近即是一个实现了SDWebImageOperation商量的类似。

当时将该operation靶在了self.runningOperations频组,表示目前正进行的操作等。

然后我们为url的字符串为key,调用SDImageCache的实例方法queryDiskCacheForKey:key done:于缓存获取获取图片,若得成功则拿其回调。

如于缓存中无找到图片,则持续为生实行至今,则开始如起网要图片了。

// 执行到此,说明缓存中并未找到图片,所以得从网络下载。
// 该方法进行异步下载图片,并返回了一个subOperation对象,它是一个实现了SDWebImageOperation协议的对象
    id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url
                                                                               options:downloaderOptions
                                                                              progress:progressBlock
                                                                             completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) {
    ...
    ...
    ...
    }];

然后在completed拨调里,若下充斥成功,则第一用该图形存入缓存,然后回调到主线程。

// 下载成功后,将图片添加到缓存中,并通过block回调。  
    if (downloadedImage && finished) {
        [self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
    }

    dispatch_main_sync_safe(^{
        completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished);
    });

同时,若该下载操作就形成,则runningOperations数组中改换除该operation:

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

而实现了operationcancelBlockblock回调:

operation.cancelBlock = ^{
        [subOperation cancel]; 
        @synchronized (self.runningOperations) {
            [self.runningOperations removeObject:weakOperation];
        }
    };

删除operation

- (void)sd_removeImageLoadOperationWithKey:(nullable NSString *)key

招来字典中因key为键的op,并删除。

结尾

SDWebImage的整系统结构,及它加载图片的笔触就以上了,看那么张图更简单清楚。但是这些呢只是大约的脉络而已,还有无限紧要的苏存部分,以及台网要部分都还未曾写,留在此起彼伏,分别另起两篇研究其缓存和网有。

取消operation

- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key

摸字典中为key为键的op,调用- (void)cancel措施。(如果是汇聚,则遍历调用cancel)。取消后会将op从字典中删去。

添加operation

- (void)sd_setImageLoadOperation:(nullable id)operation forKey:(nullable NSString *)key

先取消key相应的op,再用新想op添加至字典中。


UIView+WebCache

此分类中提供了图片下载、设置的主意,为者库中UIImageView分类和UIButton分类的sd_set...洋洋洒洒措施供支撑。还提供了展示/隐藏ActivityIndicator的方法。

- (nullable NSURL *)sd_imageURL;

取当前视图图片的url,方法通过Runtime获,因此而我们于装图片时一直利用setImage设若非下sd中的艺术,会如这主意得到之价错误。

// 取消当前视图的图片下载
- (void)sd_cancelCurrentImageLoad

点是方法会以类名为key,调用UIView+WebCacheOperation中的- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key术来取消图片下载操作。

- (void)sd_setImage:(UIImage *)image imageData:(NSData *)imageData basedOnClassOrViaCustomSetImageBlock:(SDSetImageBlock)setImageBlock {
    if (setImageBlock) {
        setImageBlock(image, imageData);
        return;
    }

#if SD_UIKIT || SD_MAC
    if ([self isKindOfClass:[UIImageView class]]) {
        UIImageView *imageView = (UIImageView *)self;
        imageView.image = image;
    }
#endif

#if SD_UIKIT
    if ([self isKindOfClass:[UIButton class]]) {
        UIButton *button = (UIButton *)self;
        [button setImage:image forState:UIControlStateNormal];
    }
#endif
}

内方法设置图片,如果产生setImageBlock则调用它来安图片,否则的说话UIImageView调用setImage:方法,UIButton调用setImage:forState:方式来安装图片。

- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholder
                           options:(SDWebImageOptions)options
                      operationKey:(nullable NSString *)operationKey
                     setImageBlock:(nullable SDSetImageBlock)setImageBlock
                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                         completed:(nullable SDExternalCompletionBlock)completedBlock;

斯点子是以此分类的中坚措施,也是UIImageView+WebCacheUIButton+WebCache倍受相继艺术设置图片时实际调用的计。

本条方式中开的重要业务是调用SDWebImageManager未遭的不二法门创建一个operation,并将以此op添加到op字典中。

方式被会以开创operation前用方面内部方法设置placeholder图片,如果安了SDWebImageDelayPlaceholder,则会跳了,最后以operation回调中,如果生充斥图片失败再安装placeholder。

术被对op的complete回调进行了拍卖,如果安了SDWebImageAvoidAutoSetImage,会一直调用completeBlock,否则的言语会利用下载的图样或占位图作为参数,调用setImageBlock,
最后调用completeBlock

顿时点儿单block的调用都是通过调用上面提到的里边方法sd_setImage:imageData:basedOnClassOrViaCustomSetImageBlock:

实际在UIImageView+WebCache惨遭调用这个办法时,setImageBlock大凡nil,除非调用的凡带来completeBlock的主意并安装了block值,否则completBlock啊会流传nil,最终以运用imageView.image = image这么的法设置图片,而未是由于外部block设置。

UIButton+WebCache遭逢调用时,setImageBlock会面产生价,其情是调用UIButtonsetImage:forState:或者setBackgroundImage:forState:方法。completBlock惟有设置否则为空。


UIImageView+WebCache

斯分类中提供了千篇一律多样sd_setImage...道供外部调用,是使用者最常接触的API,这系列措施最终还是调用到了UIView+WebCache丁之主导措施。

另外,UIImageView凡是永葆多图动画的,这个分类为提供了对应的方:

- (void)sd_setAnimationImagesWithURLs:(nonnull NSArray<NSURL *> *)arrayOfURLs {
    [self sd_cancelCurrentAnimationImagesLoad];
    __weak __typeof(self)wself = self;

    NSMutableArray<id<SDWebImageOperation>> *operationsArray = [[NSMutableArray alloc] init];

    for (NSURL *logoImageURL in arrayOfURLs) {
        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:logoImageURL options:0 progress:nil completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
            if (!wself) return;
            dispatch_main_async_safe(^{
                __strong UIImageView *sself = wself;
                [sself stopAnimating];
                if (sself && image) {
                    NSMutableArray<UIImage *> *currentImages = [[sself animationImages] mutableCopy];
                    if (!currentImages) {
                        currentImages = [[NSMutableArray alloc] init];
                    }
                    [currentImages addObject:image];

                    sself.animationImages = currentImages;
                    [sself setNeedsLayout];
                }
                [sself startAnimating];
            });
        }];
        [operationsArray addObject:operation];
    }

    [self sd_setImageLoadOperation:[operationsArray copy] forKey:@"UIImageViewAnimationImages"];
}

- (void)sd_cancelCurrentAnimationImagesLoad {
    [self sd_cancelImageLoadOperationWithKey:@"UIImageViewAnimationImages"];
}

动多只url来创造多独operation,再用这些op放入数组,最后将op数组上加到op字典中。添加op和撤回下充斥还单身供了一个key与正规的同一布置图的操作区分。

UIImageView+HighlightedWebCache及此分类类似,唯一不同之凡,它见面提供基本措施参数中之setImageBlock,其情就是安装highlightedImage

UIButton+HighlightedWebCache与这分类类似,唯一不同的凡,它见面供基本措施参数中之setImageBlock,其内容就是是安装setImage:forState:或者setBackgroundImage:forState:


UIImage+GIF

使用NSData创建Gif图片

+ (UIImage *)sd_animatedGIFWithData:(NSData *)data

判定是否是Gif

- (BOOL)isGIF {
    return (self.images != nil);
}

SDWebImagePrefetcher

预加载图片,默认在主队列,低优先级加载图片。

好经prefetcherQueueoptionsmaxConcurrentDownloads齐对预加载进行控制,可以由此代理的回调获取预加载的状态信息。


SDWebImageOperation

一个合计,里面就定义了一个撤方法。

@protocol SDWebImageOperation <NSObject>
- (void)cancel;
@end

SDImageCacheConfig

图缓存设置

  • shouldDecompressImages,是否解码图片。对下载和缓存的图形解码,可以加快图片的显示,但会消耗又多之内存。默认YES。
  • shouldDisableiCloud,是否禁用iCloud备份,默认YES,即夺。SharedSDImageCache默认存储的沙盒路径(Cache)是无见面受iCloud备份的,但是SDImageCache可以填补加起定义缓存路径,这时候就可能存储于会见叫iCloud备份的路子上,此时此选项就时有发生矣意思。
  • shouldCacheImagesInMemory,是否在内存中缓存图片,默认YES。
  • maxCacheAge,最可怜缓存时间(秒为单位),默认一到。
  • maxCacheSize,最深缓存空间(bytes),默认0。

SDImageCache

SDImageCache图片缓存,它概括外存缓存和可选的硬盘缓存,硬盘缓存写副常凡异步的,不会见阻塞UI。

- (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
                       diskCacheDirectory:(nonnull NSString *)directory

初始化,SD会在沙盒的Cache文件夹下创造命名空间同名的文件夹A,最终因为/Sandbox/Library/Caches/namespace/com.hackemist.SDWebImageCache.namespace作为硬盘缓存路径。

初始化时见面报通知,在次收到内存警告时排除所有内存缓存,在快要关张和上后台时解除过硬盘缓存。删除过期文件方式被,会事先删除过期文件(默认为保存一完美)。
过文件去后计算缓存大小,如果大小超过设置的复苏存大小限制,会延续去,优先删除最原始的图,删到缓存大小为设置大小一半结。

会创一个自定义串行队列做IO操作,所有的存入、删除、查询操作都见面于这班里好,以这来确保线程安全。获取图片并不曾控制在是行列完成。

sharedImageCache单例会以default呢命名空间初始化。

内部的AutoPurgeCache,
继承自NSCache,在吸收内存警告时,会活动删除内存中的富有缓存。SD中之外存缓存属性为NSCache,实际为利用AutoPurgeCache初始化的实例。

NSUInteger SDCacheCostForImage(UIImage *image),
计算同一摆图的休养存大小消耗。在Mac中为wid * hei,
在iOS中为wid * scale * hei * scale


- (void)addReadOnlyCachePath:(nonnull NSString *)path

添加硬盘缓存的寻路径,可以于思念预加载Bundle资源时利用。


- (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key

以key获取硬盘缓存文件之文件称,文件称是用key做MD5加密,如果后继缀名加上文件后缀。


- (void)storeImage:(nullable UIImage *)image
         imageData:(nullable NSData *)imageData
            forKey:(nullable NSString *)key
            toDisk:(BOOL)toDisk

依据参数与布局(SDImageCacheConfig)将图片缓存到内存和硬盘。

缓存到硬盘时,会操纵以初始化时创造的io队排进行操作,实际调用了

- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key

这个方法开展仓储,这个法子被见面检讨队列是否是指定的io队列,不会见再也控制代码在io队排上执行。

倘艺术无提供imageData,会以image转为Data, 最后存入硬盘的凡Data。

如安中涵盖了禁用iCloud备份,会在存入时对URL设置禁用iCloud备份。


- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock

为key获取文件路径,查询硬盘图片是否是,并无见面加载图片。


- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key {
    return [self.memCache objectForKey:key];
}

盖key查找内存缓存的图。


- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key {
    UIImage *diskImage = [self diskImageForKey:key];
    if (diskImage && self.config.shouldCacheImagesInMemory) {
        NSUInteger cost = SDCacheCostForImage(diskImage);
        [self.memCache setObject:diskImage forKey:key cost:cost];
    }

    return diskImage;
}

// inner method
- (nullable UIImage *)diskImageForKey:(nullable NSString *)key {
    // 查找硬盘缓存Data
    NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
    if (data) {
        UIImage *image = [UIImage sd_imageWithData:data];
        // 如果文件名包含@2x、@3x,会以不同的scale创建UIImage
        image = [self scaledImageForKey:key image:image];
        if (self.config.shouldDecompressImages) {
              // 解码图片
            image = [UIImage decodedImageWithImage:image];
        }
        return image;
    }
    else {
        return nil;
    }
}

取硬盘缓存的图片,查找的下会打具有的途径查找(默认路径+自定义路径),查找到Data后会依据图片种类创建不同的UIImage对象([UIImage sd_imageWithData:]),
比如Gif、WebP等,并冲文件称是否包含@2x、@3x调整image的scale。

假定安了预解码,会指向图片进行解码,解码操作会过滤调动图以及含有alpha通道的图纸。

若是设置了外存缓存,会用图纸添加到内存缓存着。


- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key

觅缓存的图片,先从内存查找,没有重新从硬盘查找。


- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key 
                                               done:(nullable SDCacheQueryCompletedBlock)doneBlock

返回一个[NSOperation new],
方法会先查找内存缓存,如果找到则坐对应的图片、Data、缓存类型等调用done回调。如果没找到会当io队列查找硬盘缓存。

使所返的NSOperation给撤除,则会已查找硬盘缓存。


- (void)removeImageForKey:(nullable NSString *)key 
                 fromDisk:(BOOL)fromDisk 
           withCompletion:(nullable SDWebImageNoParamsBlock)completion

自打缓存中删去图片。

如果设置了外存缓存,则会自外存缓存着去除图片。

如果fromDisk也真,则会在io队列中删除硬盘缓存。

掉调会在主线程给调用。


- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock

抹过期文件方式被,会优先删除过期文件(默看保存一完美)。

过文件去后计算缓存大小,如果大小超过设置的复苏存大小限制,会延续去,优先删除最老的图形,删到缓存大小也安大小一半完结。


SDWebImageDownloaderOperation

SDWebImageDownloaderOperation继承自NSOperation,实现了多只代理协议章程:

  • SDWebImageDownloaderOperationInterface,
    提供创建对象方法;添加下载过程回调、结果回调;设置是否解码图片等。
  • SDWebImageOperation,提供- (void)cancel方法。
  • NSURLSessionTaskDelegateNSURLSessionDataDelegate

是类似是生充斥图片的operation对象,它会处理NSURLSessionTaskDelegateNSURLSessionDataDelegate遭受的方法。

以此仿佛中生少数独NSURLSession类型属性:

  • unownedSession,外部提供的session,由外部管理和承担置空,代理和代办方呢鉴于外部控制落实。
  • ownedSession,当外部提供的session为空时,内部创立的session,自己担负停用和置空,自己实现代理方。

SD实际代码中,外部session由SDWebImageDownloader供以及管制,Downloader中落实了连带代理方,但是以代理方被见面调用SDWebImageDownloaderOperation兑现之系代理方来处理逻辑:

// SDWebImageDownloader中
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task];
    // 调用operation实现的代理方法
    [dataOperation URLSession:session task:task didCompleteWithError:error];
}

其一看似中维护了一个回调字典,存储了有添加的progressCallbackcompletedCallback,并创建了一个barrierQueue,将对回调字典的操作放到这个队列中来管线程安全。

好像中又写了NSOperation- (void)start- (void)cancel方,结合下载的顺序代理方lovebet爱博体育官网控制operation的finishedexecuting状态,
并根据NSOperation的文档重写了这点儿只特性的setter

@property (readonly, getter=isExecuting) BOOL executing

A Boolean value indicating whether the operation is currently executing.
The value of this property is YES if the operation is currently executing its main task or NO if it is not.

When implementing a concurrent operation object, you must override the implementation of this property so that you can return the execution state of your operation. 
In your custom implementation, you must generate KVO notifications for the isExecuting 
key path whenever the execution state of your operation object changes. For more information about manually generating KVO notifications, see Key-Value Observing Programming Guide.
You do not need to reimplement this property for nonconcurrent operations.

finished与的同。

假如实现之operation是出现的,必须重新写就点儿个属性,来自己支配op的状态,并且需要在性质改变时于有KVO通知。如果无是出新的,则无必要重写。

@implementation SDWebImageDownloaderOperation {
    @synthesize executing = _executing;
    @synthesize finished = _finished;

    - (void)setFinished:(BOOL)finished {
        [self willChangeValueForKey:@"isFinished"];
        _finished = finished;
        [self didChangeValueForKey:@"isFinished"];
    }

    - (void)setExecuting:(BOOL)executing {
        [self willChangeValueForKey:@"isExecuting"];
        _executing = executing;
        [self didChangeValueForKey:@"isExecuting"];
    }
}

重写父类属性时,需要使用到成员变量,则只要用@synthesize,否则在子类重写的settergetter受到凡造访不交的。


SDWebImageDownloader

SD中的图片下载器,与SDWebImageCache结此库房中之“下充斥”和“缓存”两死骨干。

下载选项

typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
     // 默认,影响下载操作在队列中的优先级
    SDWebImageDownloaderLowPriority = 1 << 0,
    // 渐进式下载,可以逐步显示出图片全部
    SDWebImageDownloaderProgressiveDownload = 1 << 1,

    /**
     * 默认情况下下载图片请求会设置NSURLRequestReloadIgnoringLocalCacheData缓存策略,不使用NSURLCache缓存。
     * 如果设置了这个选项,会使用NSURLRequestUseProtocolCachePolicy,启动NSURLCache缓存。
     */
    SDWebImageDownloaderUseNSURLCache = 1 << 2,

    /**
     * 忽略从NSURLCache缓存的图片,如果图片是从NSURLCache获取到,则以nil来回调
     * 可以结合`SDWebImageDownloaderUseNSURLCache`一起使用。
     */
    SDWebImageDownloaderIgnoreCachedResponse = 1 << 3,

    // 在App进入后台继续下载图片
    SDWebImageDownloaderContinueInBackground = 1 << 4,

    // 影响NSMutableURLRequest.HTTPShouldHandleCookies属性值
    SDWebImageDownloaderHandleCookies = 1 << 5,

    // 允许不被信任的SSL证书
    SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6,

    // 高优先级下载图片
    SDWebImageDownloaderHighPriority = 1 << 7,

    // 默认情况下,硬盘缓存的Data是在请求中逐渐拼接起来的NSMutableData
    // 如果设置此项,则使用UIImagePNGRepresentation(image), 此Data会比前者更小
    SDWebImageDownloaderScaleDownLargeImages = 1 << 8,
};

下载顺序

typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
    // 默认值,先进先出
    SDWebImageDownloaderFIFOExecutionOrder,


    // 后进先出
    SDWebImageDownloaderLIFOExecutionOrder
};

属性初始化

  • shouldDecompressImages, 是否解码图片,默认YES。
  • executionOrder, 队列顺序,默认先进先出。
  • maxConcurrentOperationCount,队列最可怜任务并发数,默认6只。
  • downloadTimeout,任务超时时间,默认15秒。

另外,会创一个NSURLSession因此来进展呼吁,注意,不是采用sharedSession,而是单独创建一个session。在Downloader对象销毁时会调用session的- (void)invalidateAndCancel法并置空。

另外还会见会见维护一个URLOperations字段来存储所有的operation,这个字典的操作会放在一个自营第队列中,来保管线程安全。


下载图片

- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock

使URL创建请求request,根据options参数设置request的复苏存策略(是否动NSURLCache缓存)、设置cookie处理方式、设置请求头等。

如果URL对应的SDWebImageDownloaderOperation莫有被其中字典中,则会因request创建一个op,根据options参数设置op的优先级,并补充加至队中。如果队列的实践顺序是后进先出,则用之前的最终一个op添加依赖到新的op上来确保实施顺序。

方法会返回一个Token,可以用来取消相应之op。


SDWebImageCombinedOperation

@interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>

@property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
@property (copy, nonatomic, nullable) SDWebImageNoParamsBlock cancelBlock;
@property (strong, nonatomic, nullable) NSOperation *cacheOperation;

@end

@implementation SDWebImageCombinedOperation

- (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];
    }
}

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

其一仿佛遵守SDWebImageOperation协议,实现了- (void)cancel法,并且产生一个NSOperation类型的cacheOperation性与一个cancelBlock属性。

- (void)cancel方法中,cacheOperation的撤销方法会吃调用,然后调用cencelBlock


SDWebImageManager

SD中之主干类,由其来协调SDImageCacheSDWebImageDownloader协同工作。

下载选项:

typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
    // 下载失败过的URL会从黑名单中删除并重新尝试下载
    SDWebImageRetryFailed = 1 << 0,

    // 对应SDWebImageDownloaderLowPriority,低优先级的下载任务不会在UI交互时启动
    SDWebImageLowPriority = 1 << 1,

     // 只进行内存缓存
    SDWebImageCacheMemoryOnly = 1 << 2,

    // 对应SDWebImageDownloaderProgressiveDownload,渐进式下载显示
    SDWebImageProgressiveDownload = 1 << 3,

    /**
     * 对应SDWebImageDownloaderUseNSURLCache
     * 即使图片有SD缓存,也会遵循HTTP的缓存控制,在需要的时候从远端获取图片,比如远端图片有新版本。本地硬盘缓存会由NSURLCache进行处理,而不是SDWebImage。
     * 这个在同一个URL对应的图片可能发生改变时很有用,比如一个头像图片的URL。
     * 如果缓存图片被刷新了,回调会先被调用一次,参数是缓存图片,然后用请求的图片作为参数再调用一次。
     */
    SDWebImageRefreshCached = 1 << 4,

    // 对应SDWebImageDownloaderContinueInBackground,App进入后台时继续下载图片,如果后台任务超时,任务会被取消。
    SDWebImageContinueInBackground = 1 << 5,

    /**
     * 对应SDWebImageDownloaderHandleCookies,会设置
     * NSMutableURLRequest.HTTPShouldHandleCookies = YES;
     * 来处理存储在NSHTTPCookieStore中的cookie
     */
    SDWebImageHandleCookies = 1 << 6, 会设置

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

    // 对应SDWebImageDownloaderHighPriority,高优先级任务会在下载队列中被优先下载
    SDWebImageHighPriority = 1 << 8,

    // 延迟占位图展示,默认占位图会在下载图片前显示,使用此项后,占位图会在下载任务完成后没有图片的情况下才显示
    SDWebImageDelayPlaceholder = 1 << 9,

    /** 默认SDWebImageManager是不会调用下面的代理方法预处理下载好的动图的:
     * imageManager:transformDownloadedImage:withURL:
     * 使用这个选项,manager在下载完成后、缓存图片和回调前会调用此代理方法获取处理过的图片
     */
    SDWebImageTransformAnimatedImage = 1 << 10,

    // 默认会自动将图片设置到视图上,使用此项取消自动设置
    SDWebImageAvoidAutoSetImage = 1 << 11,

    // 对应SDWebImageDownloaderScaleDownLargeImages,如果设置了`SDWebImageProgressiveDownload`,则此项会失效
    SDWebImageScaleDownLargeImages = 1 << 12
};

商章程

@class SDWebImageManager;

@protocol SDWebImageManagerDelegate <NSObject>

@optional

// 控制图片是否应该下载
- (BOOL)imageManager:(nonnull SDWebImageManager *)imageManager shouldDownloadImageForURL:(nullable NSURL *)imageURL;


// 在图片下载后、缓存图片前调用此方法处理图片,方法会在全局队列中调用
- (nullable UIImage *)imageManager:(nonnull SDWebImageManager *)imageManager transformDownloadedImage:(nullable UIImage *)image withURL:(nullable NSURL *)imageURL;

@end

- (nullable NSString *)cacheKeyForURL:(nullable NSURL *)url;

@property (nonatomic, copy, nullable) SDWebImageCacheKeyFilterBlock cacheKeyFilter;

cacheKeyForURL:返URL对应之休养生息存key,如果产生cacheKeyFilter会面使用其的返回值作为最终之价,否则回url.absoluteString


- (nullable id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                              options:(SDWebImageOptions)options
                                             progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                            completed:(nullable SDInternalCompletionBlock)completedBlock;

比方URL对应之图片未以缓存中,则开展下载。

方法会创建并返回一个SDWebImageCombinedOperation类型的operation,operation的cacheOperation是由[self.imageCache queryCacheOperationForKey:...]道返回的一个NSoperation

要欲下载图片,则会调用[self.imageDownloader downloadImageWithURL:...]艺术下充斥图片,方法返回一个SDWebImageDownloadToken项目的token,这个token会被前的operation的cancelBlock抓获,在block中利用此token来调用[self.imageDownloader cancel:token]主意开展取消。


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

将图片缓存到内存和硬盘。


- (void)cachedImageExistsForURL:(nullable NSURL *)url
                     completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;

异步确认url对应之图样是否有于缓存中,先从内存查找,否则异步在硬盘查找,回调在主线程执行。


- (void)diskImageExistsForURL:(nullable NSURL *)url
                   completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;

异步确认URL对应的图样是否有吃硬盘缓存,
不会见加载图片,回调在主线程执行。


各个Operation里的关联

于地方的逐一类吃,可以看来出好几种植Operation,和撤回方法。总结如下:

NSOperation
1. - (void)cencel;

SDWebImageOperation协议
1. - (void)cancel;

SDImageCache:
`queryCacheOperationForKey:...`方法
1. query操作首先查找内存, 命中则回调,返回nil
2. 返回NSOperation,异步查找硬盘,回调。

SDWebImageDownloaderOperation:
1. 内部维护callbackBlocks数组,数组元素为Dic<KEY,BLOCK>
2. `- (void)cancel`,调用父类NSOperation的cancel方法,调用dataTask的cancel方法,设置状态
3. `- (BOOL)cancel:(nullable id)token`,从内部维护的callbackBlocks数组中删除对应的回调,调用`- (void)cancel`方法

SDWebImageDownloader:
1. 内部维护URLOperations字典,key为URL,值为SDWebImageDownloaderOperation
2. `- (void)cancel:(nullable SDWebImageDownloadToken *)token`,
    使用token.URL从URLOperations字典中获取downloaderOp, 调用downloaderOp的
    `- (BOOL)cancel:(nullable id)token`方法。将downloaderOp从字典中删除。
3. `downloadImageWithURL:..`方法返回SDWebImageDownloadToken,
    方法内会创建SDWebImageDownloaderOperation,将downloaderOp添加到URLOperations字典中,将downloaderOp添加到队列中。
4. `- (void)cancelAllDownloads`,方法会调用`[self.downloadQueue cancelAllOperations]`,系统会对队列内的所有operation调用`- (void)cancel`方法。

SDWebImageCombinedOperation:
1. 遵守SDWebImageOperation协议
2. 有一个NSOperation类型cacheOperation属性
3. 有一个cancelBlock属性
4. `- (void)cancel`方法内会调用cancelBlock和cacheOperation的cancel方法

SDWebImageManager
1. 内部维护一个runningOperations数组,里面的对象是SDWebImageCombinedOperation
2. `loadImageWithURL:..`,会创建`SDWebImageCombinedOperation`类型的combinedOp, 
    创建combinedOp.cacheOperation为`[SDImageCache.sharedCache queryCacheOperationForKey:...]`的返回值,
    如果没有查找到缓存图片,则下载。下载会调用`[SDWebImageDownloader.sharedDownloader downloadImageWithURL:...]`方法,
    返回的SDWebImageDownloadToken被combinedOp的cancelBlock捕获。
3. 在queryCache或者downloadImage的过程中,如果combinedOp被cancel,则会被从runningOperations中删除。

相关文章