Skip to content

7. SDWebImage原理

Hannah0103 edited this page Dec 22, 2025 · 3 revisions

一、技术点

1.dispatch_barrier_sync函数,用于对操作设置顺序,确保在执行完任务后再确保后续操作。常用于确保线程安全性操作

2.NSMutableURLRequest:用于创建一个网络请求对象,可以根据需要来配置请求报头等信息

3.NSOperation及NSOperationQueue:操作队列是OC中一种告诫的并发处理方法,基于GCD实现,相对于GCD来说,操作队列的优点是可以取消在任务处理队列中的任务,另外在管理操作间的依赖关系方面容易一些,对SDWebImage中我们看到如何使用依赖将下载顺序设置成后进先出的顺序

4.NSURLSession:用于网络请求及相应处理

5.开启后台任务

6.NSCache类:一个类似于集合的容器,存储key-value对,这一点类似于nsdictionary类,我们通常用使用缓存来临时存储短时间使用但创建昂贵的对象。重用这些对象可以优化性能,因为它们的值不需要重新计算。另外一方面,这些对象对于程序来说不是紧要的,在内存紧张时会被丢弃

7.清理缓存图片的策略:特别是最大缓存空间大小的设置。如果所有缓存文件的总大小超过这一大小,则会按照文件最后修改时间的逆序,以每次一半的递归来移除那些过早的文件,直到缓存的实际大小小于我们设置的最大使用空间。

8.图片解压操作:这一操作可以查看SDWebImageDecoder.m中+decodedImageWithImage方法的实现。

9.对GIF图片的处理

10.对WebP图片的处理。

+-----------------------------------------------------------------+ | 🖼️ SDWebImage 加载流程图 | +-----------------------------------------------------------------+

                         [开始]
                            ↓
    ┌──────────────── setImageWithURL:... ────────────────┐
    │   (UIImageView+WebCache)                              │
    │ → 先显示 placeholderImage                            │
    └──────────────────────┬──────────────────────────────┘
                           ↓
       ┌───────── downloadWithURL:... (SDWebImageManager) ─────────┐
       │ → 检查缓存:调用 SDImageCache.queryDiskCacheForKey:       │
       └──────────────────────┬───────────────────────────────────┘
                              ↓
              +------------------------------+
              |     缓存查找阶段              |
              +------------------------------+
                            ↓
         ┌────────────────────────────────────┐
         │ 1. 内存缓存检查                    │
         │ → [SDImageCache objectForKey:key]  │
         └────────────────┬─────────────────┘
                          ↓
           ┌─────────────┴────────────┐
           ↓                          ↓
  【命中内存缓存】            【未命中内存缓存】
      ↓                              ↓
回调 delegate:                生成 Operation 进入队列
didFindImage:                开始硬盘查找(异步)
                               ↓
                      [NSOperation: readDataFromDiskForKey:]
                               ↓
                     尝试从磁盘读取图片文件
                               ↓
           ┌─────────────────┴─────────────────┐
           ↓                                     ↓
 【找到磁盘缓存】                       【未找到任何缓存】
     ↓                                         ↓

把图片加入内存缓存 触发下载流程: 回调 didFindImage: 创建/复用 SDWebImageDownloader ↓ 使用 NSURLConnection 发起请求 ↓ connection:didReceiveResponse: ↓ connection:didReceiveData: → 支持渐进式解码(ImageIO) ↓ connectionDidFinishLoading: ↓ 交给 SDWebImageDecoder 解码(在后台线程) ↓ 解码完成 → 回主线程通知结果 ↓ imageDecoder:didFinishDecodingImage:... ↓ imageDownloader:didFinishWithImage:... ↓ webImageManager:didFinishWithImage:... ↓ 通知所有 delegate 下载成功 ✅ ↓ 保存图片到缓存(内存 + 磁盘)💾 (磁盘写入也通过 NSInvocationOperation 异步完成)

                            ↑
                            │
              ┌─────────────┴────────────┐
              ↓                          ↓
   【展示最终图片】            【触发 completion 回调】
   [imageView setImage:image]   可用于自定义处理

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 可以预先下载图片,方便后续使用。

+-----------------------------+ | 开始缓存图片 | +-----------------------------+ ↓ +-----------------------+ | 1. 写入内存缓存 | | [Memory: NSCache] | | 快速访问,程序内有效 | +-----------------------+ ↓ +-----------------------+ | 2. 是否需磁盘缓存? | | 根据策略决定(默认开启) | +-----------------------+ ↓ yes +----------------------------------+ | 3. 添加 task 到串行 I/O 队列 | | dispatch_async(ioQueue, ^{...}) | +----------------------------------+ ↓ +----------------------------------+ | 4. 判断图片类型 | | → JPEG? PNG? 是否有 Alpha? | +----------------------------------+ ↓ +----------------------------------+ | 5. 转换为对应格式的 NSData | | UIImageJPEGRepresentation() | | UIImagePNGRepresentation() | +----------------------------------+ ↓ +----------------------------------+ | 6. 对 key 做 MD5 得到安全文件名 | | key = "http://..." | | → filename = "d41d8cd..." | +----------------------------------+ ↓ +----------------------------------+ | 7. 写入磁盘 | | [data writeToFile:path atomically:YES] | +----------------------------------+ ↓ ✅ 缓存完成!

设计选择 | 原因 内存 + 磁盘双缓存 | 平衡速度与持久性 串行队列写磁盘 | 防止并发写冲突,保护 I/O 性能 自动识别格式 | 保持画质同时节省空间 MD5 文件名 | 安全、统一、兼容性强 异步写磁盘 | 不阻塞主线程,UI 流畅

Clone this wiki locally