主要功能
- 提供 UIImageView 的一个分类,以支持网络图片的加载与缓存管理 一个异步的图片加载器。
- 一个异步的内存+磁盘图片缓存
- 支持 GIF 图片
- 支持 WebP 图片
- 后台图片解压缩处理
- 确保同一个 URL 的图片不被下载多次
- 确保虚假的 URL 不会被反复加载
- 确保下载及缓存时,主线程不被阻塞
- SDWebImage 下载的核心其实就是利用 NSURLConnection 对象来加载数据。每个图片的下 载都由一个 Operation 操作来完成,并将这些操作放到一个操作队列中。这样可以实现 图片的并发下载。
缓存
-
为了减少网络流量的消耗,我们都希望下载下来的图片缓存到本地,下次再去获取同一张图片时,可以直接从本地获取,而不再从远程服务器获取。这样做的另一个好处是提升了用户体验,用户第二次查看同一幅图片时,能快速从本地获取图片直接呈现给用户。
-
SDWebImage 提供了对图片缓存的支持,而该功能是由 SDImageCache 类来完成的。该类负责处理内存缓存及一个可选的磁盘缓存。其中磁盘缓存的写操作是异步的,这样就不会对 UI 操作造成影响。
-
SDImageCache 提供了大量方法来缓存、获取、移除及清空图片。而对于每个图片,为了方便地在内存或磁盘中对它进行这些操作,我们需要一个 key 值来索引它。
-
在内存中,我们将其作为 NSCache 的 key 值;而在磁盘中,我们用这个 key 作为图片的文件名。
-
对于一个远程服务器下载的图片,其 url 是作为这个 key 的最佳选择了。我们在后面会看到这个 key 值的重要性。
实现机制
- UIImageView+WebCache: setImageWithURL:placeholderImage:options: 先显示 placeholderImage ,同时由SDWebImageManager 根据 URL 来在本地查找图片。
- SDWebImageManager: downloadWithURL:delegate:options:userInfo: SDWebImageManager 是将 UIImageView+WebCache 同 SDImageCache 链接起来的类, SDImageCache: queryDiskCacheForKey:delegate:userInfo: 用来从缓存根据 CacheKey 查找图片是否已经在缓存中。
- 如果内存中已经有图片缓存,SDWebImageManager 会回调 SDImageCacheDelegate : imageCache:didFindImage:forKey:userInfo: ,而 UIImageView+WebCache 则回调SDWebImageManagerDelegate: webImageManager:didFinishWithImage: 来显示图片。
- 如果内存中没有图片缓存,那么生成 NSInvocationOperation 添加到队列,从硬盘查找图片 是否已被下载缓存。
- 根据 URLKey 在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作, 所以回主线程进行结果回调 notifyDelegate:
- 如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。然后 SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 展示图片。
- 如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo: 共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。
- 图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下
载失败。 - connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果。
- connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
- 图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图
片进行二次处理,最好也在这里完成,效率会好很多。 - 在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,
imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。 - imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
- 通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。
- 将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。
- 写文件到硬盘在单独 NSInvocationOperation 中完成,避免拖慢主线程。
- 如果是在iOS上运行,SDImageCache 在初始化的时候会注册notification 到
UIApplicationDidReceiveMemoryWarningNotification 以及 UIApplicationWillTerminateNotification,在内存警告的时候清理内存图片缓存,应
用结束的时候清理过期图片。 - SDWebImagePrefetcher 可以预先下载图片,方便后续使用。
1. 内存缓存
-
内存缓存的处理是使用 NSCache 对象来实现的。
-
NSCache 是一个类似于集合的容器;它存储 key-value 对,这一点类似于 NSDictionary 类。
-
我们通常用使用缓存来临时存储短时间使用但创建昂贵的对象。
-
重用这些对象可以优化性能,因为它们的值不需要重新计算;另外一方面,这些对象对于程序来说不是紧要的,在内存紧张时会被丢弃。
2. 磁盘缓存
-
磁盘缓存的处理则是使用 NSFileManager 对象来实现的。
-
图片存储的位置是位于 Cache 文件夹。另外,SDImageCache 还定义了一个串行队列,来异步存储图片。
主要使用了哪些知识点?
1. dispatch_barrier_sync
- 该方法用于对操作设置栅栏,确保在执行完任务后才会执行后续操作。
- 该方法常用于确保类的线程安全性操作。
2. NSMutableURLRequest
- 用于创建一个网络请求对象,我们可以根据需要来配置请求报头等信息。
3. NSOperation 及 NSOperationQueue
- 操作队列是 Objective-C 中一种高级的并发处理方法,它是基于 GCD 来实现的。
- 相对于 GCD 来说,操作队列的优点是可以取消任务处理队列中的任务。
- 另外在管理操作间的依赖关系方面也容易一些。
- 对 SDWebImage 中我们就看到了如何使用依赖将下载顺序设置成后进先出的顺序。
4. NSURLConnection(NSURLSession)
- 用于网络请求及响应处理。
- 在 iOS7.0 后,苹果推出了一套新的网络请求接口,即 NSURLSession 类。
5. NSCache
- 一个类似于集合的容器。
- 它存储 key-value 对,这一点类似于 NSDictionary 类。
- 我们通常用使用缓存来临时存储短时间使用但创建昂贵的对象。
- 重用这些对象可以优化性能,因为它们的值不需要重新计算。
- 另外一方面,这些对象对于程序来说不是紧要的,在内存紧张时会被丢弃。
6. 清理缓存图片的策略
- 特别是最大缓存空间大小的设置,如果所有缓存文件的总大小超过这一大小,则会按照文件最后修改时间的逆序,以每次一半的递归来移除那些过早的文件,直到缓存的实际大小小于我们设置的最大使用空间。
7. 对图片的解压缩操作
- 这一操作可以查看 SDWebImageDecoder.m 中 +decodedImageWithImage 方法的实现。
如何解决图片错位问题?
- 需要判定 cell 对应的图片地址已经改变。
- 给每一个 imageView 都绑定一个下载地址。
- 如果外界传入的下载地址改变,让 imageView 绑定的地址变成新的地址,原来的下载操作取消,开始新的下载操作。
- 如何给 imageView 绑定下载地址?
– 利用 runtime,在分类中动态的为 imageView 添加 一个属性(urlString)。