作为iOS开发者,想必对SDWebImage这个开源类库很熟悉,它是用于网络图片的下载,简易的API,高效的性能获得许多star;最近自己也开发了一个类似的图片下载功能,带着疑问去解读SDWebImage这个库感觉收获颇丰,下面就我开发中遇到的几点疑惑来看看SDWebImage是如何实现的,只是解析它的思路,代码就没去贴了。
遇到的问题
1. imageView更换图片下载的url后,调用cancel后已经起飞的NSURLSessionTask仍然可能会回调到imageView上,导致imageView显示前一个url对应的图片。
现状:NSURLSessionTask调用cancel后它的completionHandler会过一段时间才调用,这个时候imageView已经设置新的completeBlock,导致completeBlock执行后imageView就显示了前一个URL的图片
SDWebImage逻辑:它用了一个字典来存放下载的NSOperation,以图片的url为key,所以在替换imageView的url的时候会用老的url读出这个NSOperation,cancel并删除它,在下载成功的回调里面会去判断当前的这个task是否还在下载队列中,所以也就不会有下载成功的回调了
SDWebImage对这个操作简化的逻辑如下:
当imageView设置下载图片URL的时候,SDWebImageManager会建立一个NSOperation,在它里面放一个NSURLSessionTask执行,然后由SDWebImageDownloader来处理NSURLSession的回调并掌管一个存放NSOperation的数组,同时imageView在设置下载图片URL的时候,以@"imageView"为key保存了这次下载创建的对象;
当imageView再次设置图片URL的时候,imageView读出之前保存的这个对象,执行取消的操作,这个方法里面SDWebImageDownloader用老的url为key读出下载的NSOperation,执行它cancel操作,NSURLSessionTask也会执行cancel,然后SDWebImageDownloader在NSOperation数组中删除这个NSOperation,接着如上所说task cancel后,session仍然可能会有complete回调,SDWebImageDownloader会遍历存在的NSOperation数组比对NSURLSessionTask,显然由于老的url的NSOperation删除了,所以老的url回来的task肯定匹配不上,所以就不会回调到imageView上
2. 如何实现图片存储,如何控制图片缓存
- 缓存图片的读取
每当imageView设置图片下载url的时候SDWebImageManager会用这个url作为key去缓存中找,先去内存缓存NSCache(NSCache相对NSMutableDictionary有个优点,就是收到内存警告的时候,它自己会清除对象)中找,然后再去磁盘上找,SDWebImage专门建立了一个ioQueue来处理磁盘上图片数据的读写,每次的读写都先建立NSOperation放在ioQueue上异步执行,一是磁盘读写会比较耗时间,二是图片数据的解压缩比较耗资源
- 图片的缓存写入
SDWebImage写入图片的目录在Cache/default/com.hackemist.SDWebImageCache.default,文件名是以图片url的md5值,存储的文件并不是图片的原始data,而是先解析原始data生成的image,然后用iOS图片压缩接口(UIImagePNGRepresentation,UIImageJPEGRepresentation),根据图片类型是否png来进行图片的压缩,最后才写入磁盘
-
图片缓存数据的清理
上面说到图片的内存缓存用的是NSCache,所以内存的缓存数据清理比较依赖于iOS自己,SDImageCache也提供maxMemoryCost和maxMemoryCountLimit属性来直接设置给了NSCache对象;
磁盘缓存的清除则复杂多了,SDImageCache监听了app的UIApplicationDidEnterBackgroundNotification和UIApplicationWillTerminateNotification,等到这两种情况出现的时候,SDImageCache开发清理磁盘缓存,SDImageCache根据两个值去清理磁盘缓存,可以在SDImageCacheConfig中设置,maxCacheAge(默认一周)和maxCacheSize(默认0);
SDImageCache先将缓存目录下的文件以创建时间,大小,是否文件等属性建立NSDirectoryEnumerator,然后遍历所有的非文件夹文件,删除那些超过maxCacheAge的文件,并将未删除的文件存在在一个字典中,便于二次删除操作
接着判断maxCacheSize是否为默认值0,如果开发者并没有设置了这个值,磁盘缓存的清理就结束了,如果有值且大于0,那么SDImageCache会将上述的未删除的文件字典以创建时间升序的方式排序生成一个数组,然后一个一个的删除,直到当前所以文件大小为之前的一半
以上就是最近一段时间对SDWebImage的探究,里面包含的知识点太多,学习好了这个库,真的还是有帮助的