iOS开发基础125-深入探索SDWebImage
SDWebImage 源码原理解读
SDWebImage 是 iOS 经典图片加载库,主要解决网络图片异步下载、双层缓存、子线程预解码、Cell复用图片错乱等问题。
一、整体架构
分层解耦,把UI入口、调度、缓存、网络拆成独立组件。
UIView+WebCache (分类入口)
│
▼
SDWebImageManager (调度中心)
/ \
SDImageCache SDWebImageDownloader
(缓存层) (网络层)
│ │
内存缓存(NSCache) NSURLSession
磁盘缓存(文件系统) NSOperationQueue 操作队列
二、核心调用链
以最常用接口 sd_setImageWithURL: 为例:
UIView+WebCache分类
↓SDWebImageManagerloadImage:options:progress:completed:
↓- 查询
SDImageCache:先查内存,内存没有再查磁盘
✅命中 → 直接返回图片,跳过网络
❌未命中 → 执行网络下载
↓ - 交给
SDWebImageDownloader下载图片
↓ - 下载完成
├─ 图片存入内存缓存
├─ 原始二进制存入磁盘缓存
└─ 回调上层展示图片
三、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;磁盘缓存不受内存警告影响。
- 磁盘缓存:后台异步清理
- 删除过期文件
- 总大小超出上限,按最久未使用淘汰
- 清理操作全部在ioQueue执行,不阻塞UI
四、SDWebImageDownloader|网络下载模块原理
内部核心成员
downloadQueue:NSOperationQueue,默认最大并发数6,控制并发请求数量URLOperations:字典,key为URL,value是SDWebImageDownloaderOperationSDWebImageDownloaderOperation:继承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复用时,旧请求晚于新请求返回,容易出现图片错位:
- 设置新URL时,先取消该View未完成的旧请求
- 回调回来做URL一致性校验
- URL对不上直接丢弃,拒绝赋值
七、图片预解码|核心性能优化
[UIImage imageWithData:]是惰性解码,不会立刻生成位图;真正解码发生在渲染到屏幕那一刻,在主线程做大图解码会直接卡顿。
❌系统原生流程(会卡顿)
下载Data → UIImage(未解码) → 主线程渲染时才解码 → UI卡顿
✅SDWebImage优化:后台线程强制预解码
网络图片压缩Data
↓
UIImage imageWithData:(未解码)
↓
子线程通过CGContext强制解码生成位图CGImageRef
↓
得到已经解码完毕、可直接渲染的UIImage
↓
存入内存缓存,主线程直接渲染使用
八、其他关键设计
- Options位枚举
位运算组合灵活控制行为:
SDWebImageRetryFailed:失败重试SDWebImageRefreshCached:绕过本地缓存强制刷新SDWebImageCacheMemoryOnly:仅使用内存缓存,不写磁盘
-
SDImageCoder 编解码器
协议化架构,支持JPG、PNG、GIF,可扩展WebP、SVG;支持渐进式JPEG边下载边显示。 -
SDImageTransformer(5.x新增)
图片下载完成后,做裁剪、圆角、缩放等二次变换。
总结
| 组件 | 核心职责 | 关键技术 |
|---|---|---|
| SDImageCache | 缓存查询、内存+磁盘存储 | NSCache + 文件系统 + 串行IO队列 |
| SDWebImageDownloader | 网络图片下载 | NSOperation + NSURLSession + 请求去重 |
| SDWebImageManager | 调度协调缓存与下载 | 缓存优先、任务取消机制 |
| UIView+WebCache | API入口,防Cell复用错乱 | 取消旧请求 + URL校验 |
| 图片解码模块 | 性能优化 | 子线程CGContext预解码 |
核心思想:缓存优先、后台解码、请求去重、防止Cell复用图片错乱,兼顾列表滑动流畅,节省流量与内存。
将来的你会感谢今天如此努力的你!
版权声明:本文为博主原创文章,未经博主允许不得转载。

浙公网安备 33010602011771号