功能简介:
1、一个添加了web图片加载和缓存管理的UIImageView分类
2、一个异步图片下载器
3、一个异步的内存加磁盘综合存储图片并且自动处理过期图片
4、支持动态gif图
5、支持webP格式的图片
6、后台图片解压处理
7、确保同样的图片url不会下载多次
8、确保伪造的图片url不会重复尝试下载
9、确保主线程不会阻塞
工作流程
1、入口
setImageWithURL:placeholderImage:options:
会先把placeholderImage
显示,然后SDWebImageManager
根据 URL 开始处理图片。
2、进入
SDWebImageManager-downloadWithURL:delegate:options:userInfo:
,交给SDImageCache
从缓存查找图片是否已经下载queryDiskCacheForKey:delegate:userInfo:
。
3、先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,
SDImageCacheDelegate
回调imageCache:didFindImage:forKey:userInfo:
到SDWebImageManager
。
4、
SDWebImageManagerDelegate
回调webImageManager:didFinishWithImage:
到UIImageView+WebCache
等前端展示图片。
5、如果内存缓存中没有,生成
NSInvocationOperation
添加到队列开始从硬盘查找图片是否已经缓存。
6、根据
URLKey
在硬盘缓存目录下尝试读取图片文件。这一步是在NSOperation
进行的操作,所以回主线程进行结果回调notifyDelegate:
。
7、如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。
SDImageCacheDelegate
回调imageCache:didFindImage:forKey:userInfo:
。进而回调展示图片。
8、如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调
imageCache:didNotFindImageForKey:userInfo:
。
9、共享或重新生成一个下载器
SDWebImageDownloader
开始下载图片。
10、图片下载由
NSURLConnection
来做,实现相关delegate
来判断图片下载中、下载完成和下载失败。
11、
connection:didReceiveData:
中利用 ImageIO 做了按图片下载进度加载效果。connectionDidFinishLoading:
数据下载完成后交给SDWebImageDecoder
做图片解码处理。
12、图片解码处理在一个
NSOperationQueue
完成,不会拖慢主线程UI
。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
13、在主线程
notifyDelegateOnMainThreadWithInfo:
宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo:
回调给SDWebImageDownloader
。imageDownloader:didFinishWithImage:
回调给SDWebImageManager
告知图片下载完成。
14、通知所有的
downloadDelegates
下载完成,回调给需要的地方展示图片。将图片保存到SDImageCache
中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独NSInvocationOperation
完成,避免拖慢主线程。
15、
SDImageCache
在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
16、
SDWI
也提供了UIButton+WebCache
和MKAnnotationView+WebCache
,方便使用。
17、
SDWebImagePrefetcher
可以预先下载图片,方便后续使用。
源码分析
一、图片下载
SDWebImageDownloader
- 单例,图片下载器,负责图片异步下载,并对图片加载做了优化处理
- 图片的下载操作放在一个
NSOperationQueue
并发操作队列中,队列默认最大并发数是6
个;
- 每个图片对应一些回调(下载进度,完成回调等),回调信息会存在
downloader
的URLCallbacks
(一个字典,key
是url
地址,value
是图片下载回调数组)中,URLCallbacks
可能被多个线程访问,所以downloader
把下载任务放在一个barrierQueue
中,并设置屏障保证同一时间只有一个线程访问URLCallbacks
。,在创建回调URLCallbacks
的block
中创建了一个NSOperation
并添加到NSOperationQueue
中。
- 每个图片下载都是一个
operation
类,创建后添加到一个队列中,SDWebimage
定义了一个协议SDWebImageOperation
作为图片下载操作的基础协议,声明了一个cancel
方法,用于取消操作。@protocol SDWebImageOperation <NSObject> -(void)cancel; @end
- 对于图片的下载,
SDWebImageDownloaderOperation
完全依赖于NSURLConnection
类,继承和实现了NSURLConnectionDataDelegate
协议的方法connection:didReceiveResponse: connection:didReceiveData: connectionDidFinishLoading: connection:didFailWithError: connection:willCacheResponse: connectionShouldUseCredentialStorage: -connection:willSendRequestForAuthenticationChalleng -connection:didReceiveData: 方法,接受数据,创建一个CGImageSourceRef对象, 在首次获取数据时(图片width,height), 图片下载完成之前,使用CGImageSourceRef对象创建一个图片对象, 经过缩放、解压操作生成一个UIImage对象供回调使用,同时还有下载进度处理。 注:缩放:SDWebImageCompat中SDScaledImageForKey函数 解压:SDWebImageDecoder文件中decodedImageWithImage
SDWebImageDownloaderOption
- 继承自
NSOperation
类,没有简单实现main
方法,而是采用更加灵活的start
方法,以便自己管理下载的状态
start
方法中创建了下载使用的NSURLConnections
对象,开启了图片的下载,并抛出一个下载开始的通知,
- 小结:下载的核心是利用
NSURLSession
加载数据,每个图片的下载都有一个operation
操作来完成,并将这些操作放到一个操作队列中,这样可以实现图片的并发下载。
-
SDWebImageDecoder
(异步对图片进行解码)
二、缓存
减少网络流量,下载完图片后存储到本地,下载再获取同一张图片时,直接从本地获取,提升用户体验,能快速从本地获取呈现给用户。
SDWebImage
提供了对图片进行了缓存,主要由SDImageCache
完成。该类负责处理内存缓存以及一个可选的磁盘缓存,其中磁盘缓存的写操作是异步的,不会对UI
造成影响。
- 1、内存缓存及磁盘缓存
- 内存缓存的处理由
NSCache
对象实现,NSCache
类似一个集合的容器,它存储key-value
对,类似于nsdictionary
类,我们通常使用缓存来临时存储短时间使用但创建昂贵的对象,重用这些对象可以优化性能,同时这些对象对于程序来说不是紧要的,如果内存紧张就会自动释放。
- 磁盘缓存的处理使用
NSFileManager
对象实现,图片存储的位置位于cache
文件夹,另外SDImageCache
还定义了一个串行队列来异步存储图片。
SDImageCache
提供了大量方法来缓存、获取、移除及清空图片。对于图片的索引,我们通过一个key
来索引,在内存中,我们将其作为NSCache
的key
值,而在磁盘中,我们用这个key值作为图片的文件名,对于一个远程下载的图片其url
实作为这个key
的最佳选择。
- 2、存储图片
先在内存中放置一份缓存,如果需要缓存到磁盘,将磁盘缓存操作作为一个
task
放到串行队列中处理,会先检查图片格式是jpeg还是png,将其转换为响应的图片数据,最后吧数据写入磁盘中(文件名是对key值做MD5后的串)。
3、查询图片
内存和磁盘查询图片API:
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key;
- (UIImage *)imageFromDiskCacheForKey:(NSString *)key;
查看本地是否存在key指定的图片,使用一下API:
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;
4、移除图片
移除图片API:
- (void)removeImageForKey:(NSString *)key;
- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion;
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk;
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion;
5、清理图片(磁盘)
清空磁盘图片可以选择完全清空和部分清空,完全清空就是吧缓存文件夹删除。
- (void)clearDisk;
- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion;
部分清理 会根据设置的一些参数移除部分文件,主要有两个指标:文件的缓存有效期(maxCacheAge:默认是1周)和最大缓存空间大小(maxCacheSize:如果所有文件大小大于最大值,会按照文件最后修改时间的逆序,以每次一半的递归来移除哪些过早的文件,知道缓存文件总大小小于最大值),具体代码参考- (void)cleanDiskWithCompletionBlock;