下面我直接把 SDWebImage 源码里的主要类/协议逐个拆开(职责、关键属性/方法、线程/性能要点、常见扩展点 / 可定制点、源码文件位置提示),按你想读源码时最需要的顺序来写。为保证准确性,我参考了 SDWebImage 官方仓库和文档(我会在段落后加来源引用)。
参考(快捷入口):SDWebImage 官方仓库与文档。(GitHub)
先给出“总体模块图(代码角度)” — 你读源码时的地图
UIImageView+WebCache(UI 层封装) → 调用 → SDWebImageManager(协调器)
SDWebImageManager 依赖两个核心引擎:SDImageCache(缓存层) + SDWebImageDownloader / loaders(下载/加载层)
SDImageCache 内部又分为:内存缓存(NSCache 或自定义) + 磁盘缓存(文件 + 元数据) + CacheConfig + Serializer
SDWebImageDownloader 由并发下载队列 + SDWebImageDownloaderOperation 组成(支持请求合并、取消、优先级)
SDWebImageCodersManager / Coder plugins 负责解码/编码(支持 WebP、AVIF、GIF 等插件化 coder)
额外:LoadersManager(多来源加载,如:Photos、Data、URLSession)、Transformers(图片后处理)、Transition(过渡动画)、Options/Context(配置扩展点)。(sdwebimage.github.io)
1) UIImageView+WebCache / UIButton+WebCache(类别扩展)
职责
最方便的入口:把业务层的
imageView.sd_setImageWithURL:映射到 SDWebImageManager 去执行加载逻辑。处理占位图、失败回调、取消、占位动画、setImage 的主线程更新等。
关键方法 / 文件
-
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:... options:... context:... completed:...(文件:UIImageView+WebCache.m)
实现要点
创建并持有一个
SDWebImageCombinedOperation(代表单个加载任务,可以取消)。把调用上下文(options + context)传给 manager;将最终 UI 更新放在主线程。
可扩展点
- 可以通过
sd_setImage的 context 传入自定义 transformer、transition、加载优先级等。(sdwebimage.github.io)
2) SDWebImageManager(协调器 / 核心入口)
职责
决策流程的“大管家”:先查内存缓存 → 再查磁盘 → 最后发起下载/加载。
维护正在进行的加载任务去重(同 URL 多个请求合并)并分发回调。
暴露公共 API 给 UI 层和其他调用方。(sdwebimage.github.io)
关键属性
imageCache(SDImageCache)imageLoader或downloader(SDWebImageDownloader 或 SDImageLoadersManager)delegate(可选,影响缓存 key、transform 等)cacheKeyFilter、cacheSerializer(用于自定义 key 或如何写入磁盘)
关键方法
-
- (id<SDWebImageOperation>)loadImageWithURL:options:context:progress:completed:返回一个可取消的 operation(通常是
SDWebImageCombinedOperation)内部流程:构建 cacheKey → 内存查找 → 磁盘查找(异步)→ 如果没有则委托给 loader/downloader → 解码(coders)→ 缓存写入 → 完成回调。
线程/性能要点
磁盘读写与解码都在后台线程中做,主线程只负责最终回调。
支持把解码后的图片直接放到内存缓存以避免下一次再次解码(性能关键)。(sdwebimage.github.io)
可扩展点 / 常见改造
你可以替换
imageCache或imageLoader实现来自定义存储或加载策略(例如从本地相册 Photos 加载)。delegate/context 可用于对同一 URL 不同的处理(不同 transform 和不同缓存 key)。(sdwebimage.github.io)
3) SDImageCache(缓存层)
职责
- 提供内存缓存 + 磁盘缓存的统一接口。负责缓存写入、读取、清理、容量策略等。
关键文件 / 类
SDImageCache.h/.m(核心实现)SDImageCacheConfig(缓存配置)SDImageCache内部通常使用NSCache做内存缓存、使用文件系统 (disk cache) 做磁盘存储,磁盘通常按 namespace 存放,key 常用 URL 的 MD5 或由cacheKeyFilter转换。(sdwebimage.github.io)
重要方法
- (void)storeImage:imageData:forKey:toDisk:completion:- (void)diskImageForKey:completion:(异步)- (UIImage *)imageFromMemoryCacheForKey:(同步)清理方法
clearMemory,clearDisk,按年龄/大小自动清理(LRU 风格)
设计细节 / 性能点
磁盘写入通常会把原始下载数据和/或编码后的形式保存,某些情况下会保存解码后的位图数据或缩略图以加速加载。
内存缓存容量按像素或字节计算,并用 NSCache 自动回收(配合系统内存压力通知)。
支持自定义
cacheSerializer(决定写磁盘时以什么格式存)和cacheKeyFilter(决定 key)。(sdwebimage.github.io)
4) SDWebImageDownloader & SDWebImageDownloaderOperation(下载层)
职责
- 负责把网络图片数据从远端拉下来(或从其他资源加载),支持队列并发、优先级、超时、HTTP 头(Etag/If-Modified-Since)、请求合并/去重、取消、进度回调等。(GitHub)
关键类/文件
SDWebImageDownloader.h/.m(管理下载队列、配置并发)SDWebImageDownloaderOperation.h/.m(每个 URL 的实际 NSOperation,实现取消/优先级/进度/回调合并)
实现要点
SDWebImageDownloader持有一个NSOperationQueue,每个下载请求对应一个SDWebImageDownloaderOperation。请求去重:当多个请求相同 URL 到达时,会复用同一个 Operation,并把多个 completionBlock 存在 operation 里,下载完成后遍历回调(减少重复请求)。
支持
maxConcurrentDownloads、downloadTimeout等。可自定义
sessionConfiguration,支持后台下载等场景。
常见坑 / 注意
取消机制要在各方(UIImageView、manager、downloader)都能合力生效;SD 的
CombinedOperation用来聚合取消。HTTP 缓存头配合磁盘缓存可以减少流量(Etag/Last-Modified 处理通常在 downloader/operation 中)。(GitHub)
5) SDWebImageCombinedOperation / SDWebImageOperation(任务表示层)
职责
- 表示一次“复合”加载任务(可能包含磁盘读取 + 网络下载)。支持统一的取消接口。
关键点
对外返回一个
id<SDWebImageOperation>(有cancel方法),UI 层持有它用于取消(例如在 UITableViewCell 重用时)。内部实现会把 disk I/O task 与 network operation 都关联在一起;
cancel会同时取消所有子任务。
6) SDImageCoder / SDImageCodersManager(解码/编码层)
职责
解码 NSData → UIImage、以及编码 UIImage → NSData。支持图片格式插件(WebP、AVIF、GIF 动画、HEIC 等)。
SDImageCodersManager充当多个 coder 的组合管理器:按照优先级选择合适的 coder 来 decode/encode。(GitHub)
关键方法
- (UIImage *)decodedImageWithData:(NSData *)data options:(NSDictionary *)options;- (NSData *)encodedDataWithImage:(UIImage *)image format:... options:...;
实现要点
解码通常分为:解析容器(是否为 animated frames)→ 使用 ImageIO / libwebp / custom impl 解码 → 如果需要做 decompress(解码成位图),会在子线程做以避免主线程卡顿。
插件化:例如 WebP 被分离到
SDWebImageWebPCoder仓库,作为可选依赖。(GitHub)
7) SDImageLoadersManager & Loaders(加载器抽象)
职责
抽象化“从某处加载图片数据”的能力,不只限于网络(例如:Photos 框架、data URI、本地 file、asset catalog 等)。
SDImageLoadersManager会按顺序尝试多个 loader(支持插件)。
接口 / 设计
Loader 协议定义:
canRequestImageForURL:、requestImageWithURL:options:context:progress:completed:等。这让 SDWebImage 能被扩展用于更多来源。(sdwebimage.github.io)
8) Transformers / Context / Options(变换与配置)
职责
Transformer:对 UIImage 做后处理(裁剪、圆角、模糊、缩放等),并且可以把变换后的结果单独缓存(通过变换 key 来分隔)。
Options/Context:运行时可传的字典,用于配置解码选项、优先级、是否只从缓存读取、缓存策略、自定义 cache key、transition 配置等。
实现要点
Transformer 需要提供一个唯一 key,作为缓存二次区分(即同 URL + 不同 transform 视为不同缓存条目)。
Context 支持传入 coder、transformer、imageScaleFactor、animatedImageClass 等。(sdwebimage.github.io)
9) SDWebImageTransition(过渡动画)
职责
- 为图像替换提供内置/可定制的过渡,像淡入、交叉溶解等。过渡仅在主线程执行并与 UIImageView 的 setImage 结合。
关键点
- 过渡可以是自定义的动画 block,允许在 image 设置前后做动画。(sdwebimage.github.io)
10) 辅助类 / 工具类(若干)
SDWebImageDownloaderOperation(上面已讲)SDMemoryCache/SDImageCache的内部 helper(比如 metadata 读写、文件名 MD5 计算)SDImageCacheKeyFilter、SDImageCacheSerializer(接口,用于自定义 key / 磁盘序列化)SDWebImagePrefetcher(预取器:批量下载并缓存)SDWebImagePlayAnimatedImage/ AnimatedImageView(支持多帧动画展示的视图或插件)
常见源码热点(你打开文件时应该优先看的地方)
UIImageView+WebCache.m:看请求如何构造,如何取消,以及如何包装 completion。SDWebImageManager.m:核心流程(memory → disk → loader),和任务合并逻辑。SDImageCache.m:磁盘写入策略、key 生成、LRU 清理策略。SDWebImageDownloader.m&SDWebImageDownloaderOperation.m:并发/合并/取消/HTTP 缓存处理。SDImageCodersManager.m/SDImageIOCoder.m:解码实现(注意异步解码线程池)。SDImageLoadersManager+ 各种 loader(Photos、APNG、Data 等)。
这些文件通常位于仓库 SDWebImage 的 SDWebImage 子目录下;插件如 WebP 在 SDWebImageWebPCoder 仓库中。(GitHub)
对源码阅读的建议(实践技巧)
先从
UIImageView+WebCache跟进调用链到SDWebImageManager,把一个完整请求的生命周期走一遍(在断点或打印下)。关注
CombinedOperation的 cancel:重用环境(表格/集合视图)里这是最容易出问题的地方。找到磁盘缓存的写入点,查看它如何处理原始 NSData 与编码后的数据(这决定磁盘缓存的大小与 IO)。
看解码是在哪个队列做的(ImageIO / custom coder),测量主线程是否会被解码占用。
如果你关心新格式(WebP/AVIF),查看对应 plugin 的 coder 源码(通常是独立仓库)。(GitHub)
小结(回顾要点)
SDWebImage 的源码是典型的职责分离 + 插件化:Manager、Cache、Downloader、Coder、Loader、Transformer 等模块都相对独立,可替换/扩展。(sdwebimage.github.io)
性能关键点在于:内存缓存(解码后位图)+ 磁盘缓存(避免重复下载)+ 异步解码(避免主线程卡顿)+ 下载去重与并发控制。(GitHub)