相信对于广大的iOS开发者,对SDWebImage并不会陌生,这个框架通过给UIImageView和UIButton添加分类,实现一个异步下载图片并且支持缓存的功能。整个框架的接口非常简洁,每个类的分工都很明确,是很值得大家学习的。
在使用这个框架的时候,只需要提供一个下载的url和占位图就可以在回调里拿到下载后的图片:
1 | [imageview sd_setImageWithURL:[NSURL URLWithString:@"pic.jpg"] placeholderImage:[UIImage imageNamed:@"placeholder"] completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { |
而且我们还可以不设置占位图片,也可以不使用回调的block,非常灵活:
1 | //图片下载完成后直接显示下载后的图片 |
在最开始先简单介绍这个框架:
这个框架的核心类是SDWebImageManger
,在外部有UIImageView+WebCache
和 UIButton+WebCache
为下载图片的操作提供接口。内部有SDWebImageManger
负责处理和协调 SDWebImageDownloader
和 SDWebImageCache
:SDWebImageDownloader
负责具体的下载任务,SDWebImageCache
负责关于缓存的工作:添加,删除,查询缓存。
首先我们大致看一下这个框架的调用流程图:
从这个流程图里可以大致看出,该框架分为两个层:UIKit层(负责接收下载参数)和工具层(负责下载操作和缓存)。
OK~基本流程大概清楚了,我们看一下每个层具体实现吧~
##UIKit层
该框架最外层的类是UIImageView +WebCache
,我们将图片的URL,占位图片直接给这个类。下面是这个类的公共接口:
1 | // ============== UIImageView + WebCache.h ============== // |
可以看出,这个类提供的接口非常灵活,可以根据我们自己的需求来调用其中某一个方法,而这些方法到最后都会走到:
1 | // ============== UIView+ WebCache.m ============== // |
而这个方法里面,调用的是UIView+WebCache
分类的:
1 | // ============== UIView+ WebCache.m ============== // |
为什么不是UIImageView+WebCache而要上一层到UIView的分类里呢?
因为SDWebImage框架也支持UIButton的下载图片等方法,所以需要在它们的父类:UIView里面统一一个下载方法。
简单看一下这个方法的实现(省略的代码用…代替):
1 | // ============== UIView+ WebCache.m ============== // |
值得一提的是,在这一层,使用一个字典
operationDictionary
专门用作存储操作的缓存,随时添加,删除操作任务。
而这个字典是UIView+WebCacheOperation
分类的关联对象,它的存取方法使用运行时来操作:
1 | // ============== UIView+WebCacheOperation.m ============== // |
为什么不直接在
UIImageView+WebCache
里直接关联这个对象呢?我觉得这里作者应该是遵从面向对象的单一职责原则(SRP:Single responsibility principle),就连类都要履行这个职责,何况分类呢?这里作者专门创造一个分类UIView+WebCacheOperation
来管理操作缓存(字典)。
到这里,UIKit
层上面的东西都讲完了,现在开始正式讲解工具层。
工具层
上文提到过,SDWebImageManager
同时管理SDImageCache
和SDWebImageDownloader
两个类,它是这一层的老大哥。在下载任务开始的时候,SDWebImageManager
首先访问SDImageCache
来查询是否存在缓存,如果有缓存,直接返回缓存的图片。如果没有缓存,就命令SDWebImageDownloader
来下载图片,下载成功后,存入缓存,显示图片。以上是SDWebImageManager
大致的工作流程。
在详细讲解SDWebImageManager
是如何下载图片之前,我们先看一下这个类的几个重要的属性:
1 | // ============== SDWebImageManager.h ============== // |
SDWebImageManager
下载图片的方法只有一个:
1 | [SDWebImageManager.sharedManager loadImageWithURL:options:progress:completed:] |
看一下这个方法的具体实现:
1 | // ============== SDWebImageManager.m ============== // |
看完了SDWebImageManager
的回调处理,我们分别看一下SDImageCache
和SDWebImageDownloader
内部具体是如何工作的。首先看一下SDImageCache
:
SDImageCache
属性
1 | // ============== SDImageCache.m ============== // |
核心方法:查询缓存
1 | // ============== SDImageCache.m ============== // |
SDWebImageDownloader
属性
1 | // ============== SDWebImageDownloader.m ============== // |
核心方法:下载图片
1 | // ============== SDWebImageDownloader.m ============== // |
这里面还有一个addProgressCallback: progressBlock: completedBlock: forURL: createCallback:
方法,用来保存progressBlock
和completedBlock
。我们看一下这个方法的实现:
1 | // ============== SDWebImageDownloader.m ============== // |
这里真正保存两个block的方法是addHandlersForProgress: completed:
:
1 | - (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock |
到这里SDWebImage
的核心方法都讲解完毕了,其他没有讲到的部分以后会慢慢添加上去。
最后看一下一些比较零散的知识点:
1. 运行时存取关联对象:
存:
1 | objc_setAssociatedObject(self, &loadOperationKey, operations, OBJC_ASSOCIATION_RETAIN_NONATOMIC); |
取:
1 | SDOperationsDictionary *operations = objc_getAssociatedObject(self, &loadOperationKey); |
2. 数组的写操作需要加锁(多线程访问,避免覆写)
1 | //给self.runningOperations加锁 |
3. 确保在主线程的宏:
1 | dispatch_main_async_safe(^{ |
4. 设置不能为nil的参数
1 | - (nonnull instancetype)initWithCache:(nonnull SDImageCache *)cache downloader:(nonnull SDWebImageDownloader *)downloader { |
如果在参数里添加了nonnull关键字,那么编译器就可以检查传入的参数是否为nil,如果是,则编译器会有警告
5. 容错,强制转换类型
1 | if ([url isKindOfClass:NSString.class]) { |
在传入的参数为NSString时(但是方法参数要求是NSURL),自动转换为NSURL
貌似还有图片解码等内容没有详细看,以后会逐渐补充哒~
————————————————- 2018年7月17日更新 ————————————————-
注意注意!!!
笔者在近期开通了个人公众号,主要分享编程,读书笔记,思考类的文章。
- 编程类文章:包括笔者以前发布的精选技术文章,以及后续发布的技术文章(以原创为主),并且逐渐脱离 iOS 的内容,将侧重点会转移到提高编程能力的方向上。
- 读书笔记类文章:分享编程类,思考类,心理类,职场类书籍的读书笔记。
- 思考类文章:分享笔者平时在技术上,生活上的思考。
因为公众号每天发布的消息数有限制,所以到目前为止还没有将所有过去的精选文章都发布在公众号上,后续会逐步发布的。
而且因为各大博客平台的各种限制,后面还会在公众号上发布一些短小精干,以小见大的干货文章哦~
扫下方的公众号二维码并点击关注,期待与您的共同成长~