AI智能
改变未来

iOS面试 – SDWebImage(SDWebImage 的实现机制)


主要功能

  • 提供 UIImageView 的一个分类,以支持网络图片的加载与缓存管理 一个异步的图片加载器。
  • 一个异步的内存+磁盘图片缓存
  • 支持 GIF 图片
  • 支持 WebP 图片
  • 后台图片解压缩处理
  • 确保同一个 URL 的图片不被下载多次
  • 确保虚假的 URL 不会被反复加载
  • 确保下载及缓存时,主线程不被阻塞
  • SDWebImage 下载的核心其实就是利用 NSURLConnection 对象来加载数据。每个图片的下 载都由一个 Operation 操作来完成,并将这些操作放到一个操作队列中。这样可以实现 图片的并发下载。

缓存

  • 为了减少网络流量的消耗,我们都希望下载下来的图片缓存到本地,下次再去获取同一张图片时,可以直接从本地获取,而不再从远程服务器获取。这样做的另一个好处是提升了用户体验,用户第二次查看同一幅图片时,能快速从本地获取图片直接呈现给用户。

  • SDWebImage 提供了对图片缓存的支持,而该功能是由 SDImageCache 类来完成的。该类负责处理内存缓存及一个可选的磁盘缓存。其中磁盘缓存的写操作是异步的,这样就不会对 UI 操作造成影响。

  • SDImageCache 提供了大量方法来缓存、获取、移除及清空图片。而对于每个图片,为了方便地在内存或磁盘中对它进行这些操作,我们需要一个 key 值来索引它。

  • 在内存中,我们将其作为 NSCache 的 key 值;而在磁盘中,我们用这个 key 作为图片的文件名。

  • 对于一个远程服务器下载的图片,其 url 是作为这个 key 的最佳选择了。我们在后面会看到这个 key 值的重要性。

实现机制

  1. UIImageView+WebCache: setImageWithURL:placeholderImage:options: 先显示 placeholderImage ,同时由SDWebImageManager 根据 URL 来在本地查找图片。
  2. SDWebImageManager: downloadWithURL:delegate:options:userInfo: SDWebImageManager 是将 UIImageView+WebCache 同 SDImageCache 链接起来的类, SDImageCache: queryDiskCacheForKey:delegate:userInfo: 用来从缓存根据 CacheKey 查找图片是否已经在缓存中。
  3. 如果内存中已经有图片缓存,SDWebImageManager 会回调 SDImageCacheDelegate : imageCache:didFindImage:forKey:userInfo: ,而 UIImageView+WebCache 则回调SDWebImageManagerDelegate: webImageManager:didFinishWithImage: 来显示图片。
  4. 如果内存中没有图片缓存,那么生成 NSInvocationOperation 添加到队列,从硬盘查找图片 是否已被下载缓存。
  5. 根据 URLKey 在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作, 所以回主线程进行结果回调 notifyDelegate:
  6. 如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。然后 SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 展示图片。
  7. 如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo: 共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。
  8. 图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下
    载失败。
  9. connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果。
  10. connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
  11. 图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图
    片进行二次处理,最好也在这里完成,效率会好很多。
  12. 在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,
    imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。
  13. imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
  14. 通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。
  15. 将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。
  16. 写文件到硬盘在单独 NSInvocationOperation 中完成,避免拖慢主线程。
  17. 如果是在iOS上运行,SDImageCache 在初始化的时候会注册notification 到
    UIApplicationDidReceiveMemoryWarningNotification 以及 UIApplicationWillTerminateNotification,在内存警告的时候清理内存图片缓存,应
    用结束的时候清理过期图片。
  18. 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)。
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » iOS面试 – SDWebImage(SDWebImage 的实现机制)