SDWebImage 4.3.2版本源码解释

文章结构

  • 主要类结构
  • 下载过程
  • 取消下载
  • 缓存处理

主要类结构

先来看一张我自己整理的类结构图,该图只是用于理解运行过程对象之间的持有关系,如下:


核心类.png

SDWebImageManager是一个单例,主要属性:
imageCache:持有缓存处理的一个单例。
imageDownloader:持有下载任务的一个单例。
failedURLs数组:下载失败过的url会被添加进该数组中。
runningOperations数组:下载的任务信息包含在该数组中,该数组包含的元素是SDWebImageCombinedOperation类的对象。

SDWebImageCombinedOperation类中的主要属性:
cancel:任务是否被取消
downloadToken:包含在SDWebImageCombinedOperation这一层中可以获取的下载任务信息,如果需要在SDWebImageCombinedOperation这一层操作下载任务,则需要通过属性downloadToken操作。
cacheOperation:封装了一个查询缓存的任务。

SDWebImageDownloadToken类中主要属性:
downloadOperation:这个才是真正的下载任务,是一个NSOperation类的一个自定义的子类,可以控制下载任务是否完成、是否在执行、取消下载等。
downloadOperationCancelToken:该对象是一个字典,包含下载进度block和完成下载block的回调,前提是我们调用的时候有定义下载进度和完成下载回调。

SDImageCache类是一个缓存处理类。

SDWebImageDownloader是一个单例,主要属性:
downloadQueue:下载队列,所有的下载任务都放到该队列中执行。
URLOperations:用于保存在队列中的任务,下载完成就把完成的任务移除。

下载过程

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

当我们调用类似以上这些接口加载网络图片时候,内部分为几个步骤去完成添加一个网络请求任务到队列中。

  1. 调用UIView+WebCache分类中的方法
- (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
                           context:(nullable NSDictionary<NSString *, id> *)context

具体实现我就不贴出来了,篇幅太长了,之后的方法讲解中只是讲解主要功能,一律不贴代码实现,相应的代码我会上传到github,对主要的方法里面都有注释。在该方法在红首先会取消掉上次的下载任务,原因是防止tableviewCell复用时候防止数据混乱。然后调用SDWebImageManager的下载方法

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

该方法会返回一个SDWebImageCombinedOperation对象,然后在UIView+WebCache分类中通过运行时方法维护一个NSMapTable对象,该NSMapTable就存着这个返回的SDWebImageCombinedOperation对象,注意这里初始化NSMapTable时代码如下

[[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];

该NSMapTable对值并不持有,SDWebImageCombinedOperation对象会被SDWebImageManager的runningOperations数组持有,当该任务下载完成后,会从该数组中移除,然后NSMapTable中的相应对象就会被释放。在UIView+WebCache分类中持有SDWebImageCombinedOperation对象只是为了取消下载任务,后续会在取消下载中讲解。

  1. 调用SDWebImageManager的下载方法
- (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url
                                     options:(SDWebImageOptions)options
                                    progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                   completed:(nullable SDInternalCompletionBlock)completedBlock

该方法主要是检查url的合法性、检查url是否已经在下载失败数组failedURLs中,如果url不合法或者url是一个下载失败过的url且没有添加SDWebImageRetryFailed标记,则立刻返回完成回调,附带有相关error信息。
如果以上步骤没问题,则会把SDWebImageCombinedOperation对象放进SDWebImageManager的runningOperations数组中,检查该url是否有缓存,如果有则回调完成,如果没有缓存,则调用 SDWebImageDownloader单例开辟下载任务。

  1. 调用SDImageCache的检查缓存方法
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock

缓存检查是开辟子线程去处理的,防止阻塞UI,也可以通过标记SDImageCacheQueryDiskSync进行同步缓存检查,不过默认是异步的,该方法的主要实现过程会在缓存处理里说明。

  1. 调用SDWebImageDownloader的下载方法
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock 

该方法会调用另外一个方法

- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
                                           completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
                                                   forURL:(nullable NSURL *)url
                                           createCallback:(SDWebImageDownloaderOperation *(^)(void))createCallback

初始化完成一个SDWebImageDownloadToken对象,可以回到文章前面的主要类查看该对象和其他类之间的关系。
在该方法中生成真正的下载任务对象,即SDWebImageDownloaderOperation对象,并把该下载任务对象保存在SDWebImageDownloader的URLOperations属性中,URLOperations是一个字典维护在下载队列中的任务,完成下载后,会把对应的任务从该 数组中移除。所以在请求一个下载任务时候,会通过URLOperations字典检查某任务是否已经在 下载队列中,如果不在就添加一个任务,防止对同一个任务重复请求。

  1. 调用SDWebImageDownloaderOperation的注册回调方法
- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                            completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock 

该方法就是为了让SDWebImageDownloaderOperation对象持有下载进度progressBlock和完成下载completedBlock回调。

取消下载

每次对控件加载网络图片时候,第一步就是取消之前的下载任务。

  1. 调用UIView+WebCache分类里的取消方法
- (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key 

在该分类里维护的NSMapTable中查找是否存在下载任务(在文章开头有说明该层次的下载信息存放在SDWebImageCombinedOperation对象中,且遵循SDWebImageOperation协议),有的话就进行下一步。

  1. 调用SDWebImageCombinedOperation对象实现的SDWebImageOperation协议,该协议就是一个cancel方法,如下:
@protocol SDWebImageOperation <NSObject>
- (void)cancel;
@end

在该方法中把SDWebImageCombinedOperation中的属性cancel置为YES、取消查询缓存任务、通过自己持有的SDWebImageManager对象调用下载对象SDWebImageDownloader的取消下载任务、把SDWebImageCombinedOperation对象从runningOperations数组中移除。

  1. 调用SDWebImageDownloader的取消方法
- (void)cancel:(nullable SDWebImageDownloadToken *)token;

在该方法里会调用SDWebImageDownloaderOperation对象的方法

- (BOOL)cancel:(nullable id)token 

该方法传入的其实是一个字典(存放着下载进度和完成下载回调,如果有的话), 然后把任务从URLOperations字典中移除

  1. 调用SDWebImageDownloaderOperation的取消方法
- (BOOL)cancel:(nullable id)token

该方法会把回调从自己的callbackBlocks属性中移除,最重要的是把SDWebImageDownloaderOperation对象的executing置为NO,finished置为YES。真正的取消下载,才是SDWebImageDownloaderOperation对象中的实现,以上3步只是把取消指令传达给SDWebImageDownloaderOperation对象而已。
类与类之间的消息传递,尽量不越过两个类,层层递进,思路非常清晰,是不是感觉作者设计得很巧妙啊-_-。

缓存处理

SDImageCacheConfig该类负责缓存的各种属性设置,如缓存多长时间、是否内存缓存、是否对图片解压等,默认缓存一个星期。
SDImageCache缓存的处理类,主要方法如下:

  1. 查询缓存
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key 
                                                 options:(SDImageCacheOptions)options 
                                                     done:(nullable SDCacheQueryCompletedBlock)doneBlock

根据key查询内存缓存,如果存在且不执行磁盘缓存则立即返回缓存对象;否则创建一个NSOperation对象执行磁盘缓存查找。如果内存缓存不存在而磁盘缓存存在,则把图片数据缓存到内存缓存。执行磁盘缓存查找时候是异步的,如果标记SDImageCacheQueryDiskSync,则同步查询 磁盘缓存,这样会影响性能。

  1. 保存缓存
- (void)storeImage:(nullable UIImage *)image
         imageData:(nullable NSData *)imageData
            forKey:(nullable NSString *)key
            toDisk:(BOOL)toDisk
        completion:(nullable SDWebImageNoParamsBlock)completionBlock

如果允许内存缓存则,先把图片缓存到内存缓存,再异步保存到磁盘

  1. 注册通知执行磁盘缓存处理
    SDImageCache在初始化时候回注册两个通知,一个应用即将退出通知,一个进入后台通知
// Subscribe to app events
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(deleteOldFiles)
                                                     name:UIApplicationWillTerminateNotification
                                                   object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(backgroundDeleteOldFiles)
                                                     name:UIApplicationDidEnterBackgroundNotification
                                                   object:nil];

这两个通知最终执行的都是下面这个方法

- (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock 

先删除最后改动日期超过一个星期的文件,如果删除后文件总大小还是比允许缓存的最大值大, 那就继续删除直到文件总大小为允许缓存的最大值的一半。

  1. 注册通知执行内存缓存处理
[[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(didReceiveMemoryWarning:)
                                                     name:UIApplicationDidReceiveMemoryWarningNotification
                                                   object:nil];

当收到内存警告通知时,会把内存缓存全部清理掉。

最后,我把SDWebImage里添加过 注释的类标注如下:


注释类.png

我已经上传到github

期待:如果有误,或者解释不清楚,可以给我留言,谢谢你的垂阅!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,258评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,335评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,225评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,126评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,140评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,098评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,018评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,857评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,298评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,518评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,400评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,993评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,638评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,661评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容