iOS开发基础125-深入探索SDWebImage

SDWebImage 源码原理解读

SDWebImage 是 iOS 经典图片加载库,主要解决网络图片异步下载、双层缓存、子线程预解码、Cell复用图片错乱等问题。

一、整体架构

分层解耦,把UI入口、调度、缓存、网络拆成独立组件。

UIView+WebCache (分类入口)
        │
        ▼
SDWebImageManager (调度中心)
       /            \
SDImageCache        SDWebImageDownloader
(缓存层)             (网络层)
    │                      │
内存缓存(NSCache)      NSURLSession
磁盘缓存(文件系统)     NSOperationQueue 操作队列

二、核心调用链

以最常用接口 sd_setImageWithURL: 为例:

  1. UIView+WebCache 分类
    ↓
  2. SDWebImageManager loadImage:options:progress:completed:
    ↓
  3. 查询 SDImageCache:先查内存,内存没有再查磁盘
      ✅命中 → 直接返回图片,跳过网络
      ❌未命中 → 执行网络下载
    ↓
  4. 交给 SDWebImageDownloader 下载图片
    ↓
  5. 下载完成
      ├─ 图片存入内存缓存
      ├─ 原始二进制存入磁盘缓存
      └─ 回调上层展示图片

三、SDImageCache|缓存模块实现原理

双层缓存:内存缓存追求读取速度;磁盘缓存实现持久化。

1. 两层缓存

  • 内存缓存:NSCache,key为图片URL,value为已解码UIImage
  • 磁盘缓存:沙盒文件存储;文件名 = URL的MD5哈希值,规避URL特殊字符造成文件名异常

2. 查询流程 queryDiskCacheForKey:

查询缓存
├─ 查内存 NSCache(同步读取)
│   └─ 命中 → 直接回调
└─ 未命中 → ioQueue异步串行读取磁盘
    ├─ 磁盘命中:读文件Data → 解码 → 写入内存缓存 → 回调
    └─ 磁盘未命中 → 返回nil,触发网络下载

3. 磁盘存储细节

  • 路径:Cache目录/com.hackemist.SDImageCache/<MD5(URL)>
  • 全部磁盘IO跑在独立串行队列ioQueue,不会阻塞主线程

4. 缓存清理策略

  • 内存缓存:监听内存警告通知,收到警告直接清空NSCache;磁盘缓存不受内存警告影响。
  • 磁盘缓存:后台异步清理
    1. 删除过期文件
    2. 总大小超出上限,按最久未使用淘汰
    3. 清理操作全部在ioQueue执行,不阻塞UI

四、SDWebImageDownloader|网络下载模块原理

内部核心成员

  • downloadQueue:NSOperationQueue,默认最大并发数6,控制并发请求数量
  • URLOperations:字典,key为URL,value是SDWebImageDownloaderOperation
  • SDWebImageDownloaderOperation:继承NSOperation,内部包装NSURLSessionDataTask做网络请求

请求去重机制

同一个URL被多个页面同时请求:

  • 不会发起多次网络请求,复用同一个Operation
  • 通过callbacksHolder保存多组回调;下载完成批量执行所有回调,一次下载多处复用结果。

下载优先级与后台下载

  • 通过NSOperation的priority控制任务优先级,调度队列执行顺序
  • 支持后台Session,App退后台依旧可以继续下载图片

五、SDWebImageManager|调度中心

门面层,上层只和Manager打交道,隔离缓存、下载实现细节。

核心方法:loadImage:options:context:progress:completed:

1. 查询SDImageCache缓存
   ├─ 命中 → 直接回调,跳过网络
   └─ 未命中 → 进入下载
2. 调用SDWebImageDownloader下载
   ├─ 下载过程:持续抛出进度回调progress
   ├‑ 下载成功:写入内存、磁盘缓存,回调图片
   └‑ 下载失败:按策略处理失败,回调错误
3. 返回Operation token,供外部取消任务

取消机制

返回SDWebImageOperation令牌;外部可调用cancel;内部从队列、回调列表移除任务,终止回调。

六、UIImageView+WebCache|UI分类入口

sd_setImageWithURL:完整逻辑

1. sd_cancelCurrentImageLoad:取消View上正在跑的旧下载
2. 设置占位图placeholder
3. 调用SDWebImageManager发起加载
4. 将operation token绑定到UIView
5. 回调使用weak self防止循环引用
   ├─ 请求URL和当前View的URL匹配 → 设置image
   └─ URL不匹配 → 直接丢弃结果,不刷新UI

Cell复用防图片错乱三层防护

列表快速滚动Cell复用时,旧请求晚于新请求返回,容易出现图片错位:

  1. 设置新URL时,先取消该View未完成的旧请求
  2. 回调回来做URL一致性校验
  3. URL对不上直接丢弃,拒绝赋值

七、图片预解码|核心性能优化

[UIImage imageWithData:]是惰性解码,不会立刻生成位图;真正解码发生在渲染到屏幕那一刻,在主线程做大图解码会直接卡顿。

❌系统原生流程(会卡顿)

下载Data → UIImage(未解码) → 主线程渲染时才解码 → UI卡顿

✅SDWebImage优化:后台线程强制预解码

网络图片压缩Data
        ↓
UIImage imageWithData:(未解码)
        ↓
子线程通过CGContext强制解码生成位图CGImageRef
        ↓
得到已经解码完毕、可直接渲染的UIImage
        ↓
存入内存缓存,主线程直接渲染使用

八、其他关键设计

  1. Options位枚举
    位运算组合灵活控制行为:
  • SDWebImageRetryFailed:失败重试
  • SDWebImageRefreshCached:绕过本地缓存强制刷新
  • SDWebImageCacheMemoryOnly:仅使用内存缓存,不写磁盘
  1. SDImageCoder 编解码器
    协议化架构,支持JPG、PNG、GIF,可扩展WebP、SVG;支持渐进式JPEG边下载边显示。

  2. SDImageTransformer(5.x新增)
    图片下载完成后,做裁剪、圆角、缩放等二次变换。

总结

组件 核心职责 关键技术
SDImageCache 缓存查询、内存+磁盘存储 NSCache + 文件系统 + 串行IO队列
SDWebImageDownloader 网络图片下载 NSOperation + NSURLSession + 请求去重
SDWebImageManager 调度协调缓存与下载 缓存优先、任务取消机制
UIView+WebCache API入口,防Cell复用错乱 取消旧请求 + URL校验
图片解码模块 性能优化 子线程CGContext预解码

核心思想:缓存优先、后台解码、请求去重、防止Cell复用图片错乱,兼顾列表滑动流畅,节省流量与内存。

posted @ 2024-07-17 17:21  Mr.陳  阅读(418)  评论(0)    收藏  举报