Cooler开源库学习系列之SDWebImage
问题1 如何设计图像的异步加载缓存机制 or SDWebImage是如何实现的
继续提问
问题2:能画出整体的框架流程图吗?
问题3:如何实现异步加载?
问题4:如何进行缓存?
问题5: 还需要考虑什么因素
以下分析的SDWebImage版本是3.7
SDWebImage 初印象
介绍
Features
- Categories for UIImageView, UIButton, MKAnnotationView adding web image and cache management
- An asynchronous image downloader
- An asynchronous memory + disk image caching with automatic cache expiration handling
- A background image decompression
- A guarantee that the same URL won’t be downloaded several times
- A guarantee that bogus URLs won’t be retried again and again
- A guarantee that main thread will never be blocked
- Performances!
- Use GCD and ARC
粗体部分为基本功能,除此之外,我们还希望关注做了哪些性能优化
Supported Image Formats
- Image formats supported by UIImage (JPEG, PNG, …), including GIF
- WebP format, including animated WebP (use the WebP subspec)
GIF是4.0之后新增的功能,没有自己实现,依赖FLAnimatedImage
Requirements
- iOS 7.0 or later
- tvOS 9.0 or later
- watchOS 2.0 or later
- OS X 10.8 or later
- Xcode 7.3 or later
使用方法
|
|
引入UIImageView+WebCache.h文件后直接在ImageView调用sd_setImageWithURL方法就可以实现图片的异步下载。除了UIImageView之外,UIButton、UIImage也支持这个扩展
流程图
通过流程图,可以大体看到WebCache的方法提供UIView的异步下载方法,SDWebImageManager 对下载进行管理,是从缓存获取(SDImageCache)还是进行下载(SDWebImageDownloader)
SDWebImage 深入剖析
源码分析
这里介绍源码实际调用流程,虽然我们经常用sd_setImageWithURL:placeholderImage方法,实际我们这个代理方法最终调用的都是UIView的这个扩展
我们就从这里开始,进行源码分析
调用流程
UIImageView+WebCache.h
Mark 1
主要是取消 Mark 7
处添加的Operation( UIView+WebCacheOperation.h),后面会详解。Mark 2
就是为了保存当前的URL,通过sd_imageURL方法可以获取当前View的URL
问题6: 如果当前View调用多个不同的URL,是否出现覆盖情况?
Mark 3
主要是设置加载前的默认图。SDWebImageDelayPlaceholder是个位枚举类型,因此支持& | 的操作
支持11中不同类型的组合,详细见SDWebImageManager.h
Mark 4
是通过 SDWebImageManager的类方法创建一个operation,传入URL,options,progressBlock和completedBlock,这些参数是SDWebImageDowdloader需要的
Mark 5
是开始下载之后结束之后的回调,不管成功还是失败。但是如果operation取消了,那么这个回调将不再会调用
问题 7
: 如果继续执行会有什么问题?
如果相同的UIView进行多次回调,那么相当于对同一个Image进行多次写操作,存在资源竞争或者覆盖真正的数据,// See #699 for more details
|
|
Mark 6
正常的回调Block,这里之所以列出来是想问一下,finish是表示是否完成,finish都是YES,那么什么情况是NO那,看了下代码,发现当URL为空,complete不为空时为NO。这个地方需要这个参数吗?[疑问脸]
UIView+WebCacheOperation.h
通过key关联图片下载的Operation,主要有3个函数:添加、取消、删除Mark 7
如果有key先取消,在添加
那么Operation是怎么执行起来的?看 Mark 4
生成的Opetaion
|
|
- SDWebImageManager.h
这段代码省略了N中情况,但是主体的处理逻辑都是相似的。
Mark 8
runningOperations
总共定义了4个属性,SDImageCache负责缓存,SDWebImageDownloader负责下载,failedURLs是失败的URL,主要减少失败URL的请求,runningOperations是SDWebImageCombinedOperation类的可变数组,主要存储正在运行的operation,在对外暴露的cancelAll和isRunning方法都会用到。
Mark 9
通过cacheKeyForURL方法取出的key主要是用于缓存,这里之所以列出一是为了说cacheKeyFilter这个属性,可以缓存的URL进行处理(过滤,去除Query信息等)
Mark 10
这里可以看出外部操作是SDWebImageCombinedOperation,这其实是服从SDWebImageOperation协议的NSObject子类,其实调用是内部的SubOperation相关下载和取消操作,之所以这样实现,是为了如果内部实现更换,对外的API可以不受影响
SDWebImageCombinedOperation
实际进行下载操作是在SDWebImageDownloader.h类中
这些属性都会在下面的方法中进行说明。
这里调用addProgressCallback
|
|
代码解释的很清楚,从中可以找到下面问题的答案:请求多次相同URL如何处理?
URLCallbacks这个字典以URL为key,value是Dictionary,这里面包含了progressBlock和CompleteBlock,其中createCallback();只会在第一次调用,因为涉及到读写问题(读是在createCalbBack),因此需要barrier保证线程安全。
现在看createCallback
中的内容
|
|
Mark 11
headersFilter 这里可以设置HTTP header fields, 可以进行公司指定域名过滤
Mark 12
创建一个SDWebImageDownloaderOperation,其中参数都是该类创建必要的参数,在progressBlock,completeBlock,cancellBlock都是通过Barrier取出callBackURL的dictionary,进行相应操作,看addProgressCallback: andCompletedBlock: forURL: createCallback:
Mark 13
NSURLCredential主要用于身份认证,通过userName和password也可以看出来,我个人用的比较少,以后在多看一下
Mark 14
这里添加到NSOperationQueue里,这时operation就会“SDWebImageDownloaderOperation”中的start方法,进行下载
Mark 15
这里通过依赖模拟实现了LIFO
最后在介绍最后一部分
>
SDWebImageDownloaderOperation.h, 就是通过NSURLConnection进行资源的下载,同时在不同的阶段,并且进行相应的通知,这里的代码,大家可以自行阅读,
细节
- 使用SDWebImageManager,下载缓存。
|
|
这对于我们自己封装UIImageView支持更丰富的默认图,点击加载等功能,提供了更加便捷的方法
- 直接使用SDWebImageDownloader进行图片异步下载,而不仅仅局限于UIImageView的image赋值
|
|
- 同样,我们可以使用SDImageCache进行图片的缓存
|
|
- cacheKeyFilter: 过滤query,可以在didFinishLaunchingWithOptions方法中添加,即在还没开始下载时进行设置。1234SDWebImageManager.sharedManager.cacheKeyFilter = ^(NSURL *url) {url = [[NSURL alloc] initWithScheme:url.scheme host:url.host path:url.path];return [url absoluteString];};
SDWebImage各类之间的关系
经过上面分析,在整体看一下各个类之间的关系图
写在最后
前面的问题的答案都已经在文章体现了,这里不作总结了,希望大家能有自己的理解总结一下。
这里稍微说一下还需要考虑的问题
1、image的类型, NSData+ImageContentType.h
2、为什么用NSCache, 不用NSDictionary
3、encode decode SDWebImageDecoder.h
至此,SDWebImage的源代码已经分析的差不多了,稍微粗糙一些,但是对于源码中需要注意的地方或者涉及的原因,结合自己的理解加以解释。如果文中有不正确的地方欢迎大家指正。