SDWebImage 源碼解析

下面我直接把 SDWebImage 源碼里的主要類/協(xié)議逐個拆開(職責(zé)、關(guān)鍵屬性/方法、線程/性能要點(diǎn)、常見擴(kuò)展點(diǎn) / 可定制點(diǎn)、源碼文件位置提示),按你想讀源碼時最需要的順序來寫。為保證準(zhǔn)確性,我參考了 SDWebImage 官方倉庫和文檔(我會在段落后加來源引用)。

參考(快捷入口):SDWebImage 官方倉庫與文檔。(GitHub)

先給出“總體模塊圖(代碼角度)” — 你讀源碼時的地圖

  • UIImageView+WebCache(UI 層封裝) → 調(diào)用 → SDWebImageManager(協(xié)調(diào)器)

  • SDWebImageManager 依賴兩個核心引擎:SDImageCache(緩存層) + SDWebImageDownloader / loaders(下載/加載層)

  • SDImageCache 內(nèi)部又分為:內(nèi)存緩存(NSCache 或自定義) + 磁盤緩存(文件 + 元數(shù)據(jù)) + CacheConfig + Serializer

  • SDWebImageDownloader 由并發(fā)下載隊列 + SDWebImageDownloaderOperation 組成(支持請求合并、取消、優(yōu)先級)

  • SDWebImageCodersManager / Coder plugins 負(fù)責(zé)解碼/編碼(支持 WebP、AVIF、GIF 等插件化 coder)

  • 額外:LoadersManager(多來源加載,如:Photos、Data、URLSession)、Transformers(圖片后處理)、Transition(過渡動畫)、Options/Context(配置擴(kuò)展點(diǎn))。(sdwebimage.github.io)


1) UIImageView+WebCache / UIButton+WebCache(類別擴(kuò)展)

職責(zé)

  • 最方便的入口:把業(yè)務(wù)層的 imageView.sd_setImageWithURL: 映射到 SDWebImageManager 去執(zhí)行加載邏輯。

  • 處理占位圖、失敗回調(diào)、取消、占位動畫、setImage 的主線程更新等。

關(guān)鍵方法 / 文件

  • - (void)sd_setImageWithURL:(NSURL *)url placeholderImage:... options:... context:... completed:...(文件:UIImageView+WebCache.m

實(shí)現(xiàn)要點(diǎn)

  • 創(chuàng)建并持有一個 SDWebImageCombinedOperation(代表單個加載任務(wù),可以取消)。

  • 把調(diào)用上下文(options + context)傳給 manager;將最終 UI 更新放在主線程。

可擴(kuò)展點(diǎn)

  • 可以通過 sd_setImage 的 context 傳入自定義 transformer、transition、加載優(yōu)先級等。(sdwebimage.github.io)

2) SDWebImageManager(協(xié)調(diào)器 / 核心入口)

職責(zé)

  • 決策流程的“大管家”:先查內(nèi)存緩存 → 再查磁盤 → 最后發(fā)起下載/加載。

  • 維護(hù)正在進(jìn)行的加載任務(wù)去重(同 URL 多個請求合并)并分發(fā)回調(diào)。

  • 暴露公共 API 給 UI 層和其他調(diào)用方。(sdwebimage.github.io)

關(guān)鍵屬性

  • imageCache(SDImageCache)

  • imageLoaderdownloader(SDWebImageDownloader 或 SDImageLoadersManager)

  • delegate(可選,影響緩存 key、transform 等)

  • cacheKeyFilter、cacheSerializer(用于自定義 key 或如何寫入磁盤)

關(guān)鍵方法

  • - (id<SDWebImageOperation>)loadImageWithURL:options:context:progress:completed:

    • 返回一個可取消的 operation(通常是 SDWebImageCombinedOperation

    • 內(nèi)部流程:構(gòu)建 cacheKey → 內(nèi)存查找 → 磁盤查找(異步)→ 如果沒有則委托給 loader/downloader → 解碼(coders)→ 緩存寫入 → 完成回調(diào)。

線程/性能要點(diǎn)

  • 磁盤讀寫與解碼都在后臺線程中做,主線程只負(fù)責(zé)最終回調(diào)。

  • 支持把解碼后的圖片直接放到內(nèi)存緩存以避免下一次再次解碼(性能關(guān)鍵)。(sdwebimage.github.io)

可擴(kuò)展點(diǎn) / 常見改造

  • 你可以替換 imageCacheimageLoader 實(shí)現(xiàn)來自定義存儲或加載策略(例如從本地相冊 Photos 加載)。

  • delegate/context 可用于對同一 URL 不同的處理(不同 transform 和不同緩存 key)。(sdwebimage.github.io)


3) SDImageCache(緩存層)

職責(zé)

  • 提供內(nèi)存緩存 + 磁盤緩存的統(tǒng)一接口。負(fù)責(zé)緩存寫入、讀取、清理、容量策略等。

關(guān)鍵文件 / 類

  • SDImageCache.h/.m(核心實(shí)現(xiàn))

  • SDImageCacheConfig(緩存配置)

  • SDImageCache 內(nèi)部通常使用 NSCache 做內(nèi)存緩存、使用文件系統(tǒng) (disk cache) 做磁盤存儲,磁盤通常按 namespace 存放,key 常用 URL 的 MD5 或由 cacheKeyFilter 轉(zhuǎn)換。(sdwebimage.github.io)

重要方法

  • - (void)storeImage:imageData:forKey:toDisk:completion:

  • - (void)diskImageForKey:completion:(異步)

  • - (UIImage *)imageFromMemoryCacheForKey:(同步)

  • 清理方法 clearMemory, clearDisk,按年齡/大小自動清理(LRU 風(fēng)格)

設(shè)計細(xì)節(jié) / 性能點(diǎn)

  • 磁盤寫入通常會把原始下載數(shù)據(jù)和/或編碼后的形式保存,某些情況下會保存解碼后的位圖數(shù)據(jù)或縮略圖以加速加載。

  • 內(nèi)存緩存容量按像素或字節(jié)計算,并用 NSCache 自動回收(配合系統(tǒng)內(nèi)存壓力通知)。

  • 支持自定義 cacheSerializer(決定寫磁盤時以什么格式存)和 cacheKeyFilter(決定 key)。(sdwebimage.github.io)


4) SDWebImageDownloader & SDWebImageDownloaderOperation(下載層)

職責(zé)

  • 負(fù)責(zé)把網(wǎng)絡(luò)圖片數(shù)據(jù)從遠(yuǎn)端拉下來(或從其他資源加載),支持隊列并發(fā)、優(yōu)先級、超時、HTTP 頭(Etag/If-Modified-Since)、請求合并/去重、取消、進(jìn)度回調(diào)等。(GitHub)

關(guān)鍵類/文件

  • SDWebImageDownloader.h/.m(管理下載隊列、配置并發(fā))

  • SDWebImageDownloaderOperation.h/.m(每個 URL 的實(shí)際 NSOperation,實(shí)現(xiàn)取消/優(yōu)先級/進(jìn)度/回調(diào)合并)

實(shí)現(xiàn)要點(diǎn)

  • SDWebImageDownloader 持有一個 NSOperationQueue,每個下載請求對應(yīng)一個 SDWebImageDownloaderOperation。

  • 請求去重:當(dāng)多個請求相同 URL 到達(dá)時,會復(fù)用同一個 Operation,并把多個 completionBlock 存在 operation 里,下載完成后遍歷回調(diào)(減少重復(fù)請求)。

  • 支持 maxConcurrentDownloads、downloadTimeout 等。

  • 可自定義 sessionConfiguration,支持后臺下載等場景。

常見坑 / 注意

  • 取消機(jī)制要在各方(UIImageView、manager、downloader)都能合力生效;SD 的 CombinedOperation 用來聚合取消。

  • HTTP 緩存頭配合磁盤緩存可以減少流量(Etag/Last-Modified 處理通常在 downloader/operation 中)。(GitHub)


5) SDWebImageCombinedOperation / SDWebImageOperation(任務(wù)表示層)

職責(zé)

  • 表示一次“復(fù)合”加載任務(wù)(可能包含磁盤讀取 + 網(wǎng)絡(luò)下載)。支持統(tǒng)一的取消接口。

關(guān)鍵點(diǎn)

  • 對外返回一個 id<SDWebImageOperation>(有 cancel 方法),UI 層持有它用于取消(例如在 UITableViewCell 重用時)。

  • 內(nèi)部實(shí)現(xiàn)會把 disk I/O task 與 network operation 都關(guān)聯(lián)在一起;cancel 會同時取消所有子任務(wù)。


6) SDImageCoder / SDImageCodersManager(解碼/編碼層)

職責(zé)

  • 解碼 NSData → UIImage、以及編碼 UIImage → NSData。支持圖片格式插件(WebP、AVIF、GIF 動畫、HEIC 等)。

  • SDImageCodersManager 充當(dāng)多個 coder 的組合管理器:按照優(yōu)先級選擇合適的 coder 來 decode/encode。(GitHub)

關(guān)鍵方法

  • - (UIImage *)decodedImageWithData:(NSData *)data options:(NSDictionary *)options;

  • - (NSData *)encodedDataWithImage:(UIImage *)image format:... options:...;

實(shí)現(xiàn)要點(diǎn)

  • 解碼通常分為:解析容器(是否為 animated frames)→ 使用 ImageIO / libwebp / custom impl 解碼 → 如果需要做 decompress(解碼成位圖),會在子線程做以避免主線程卡頓。

  • 插件化:例如 WebP 被分離到 SDWebImageWebPCoder 倉庫,作為可選依賴。(GitHub)


7) SDImageLoadersManager & Loaders(加載器抽象)

職責(zé)

  • 抽象化“從某處加載圖片數(shù)據(jù)”的能力,不只限于網(wǎng)絡(luò)(例如:Photos 框架、data URI、本地 file、asset catalog 等)。

  • SDImageLoadersManager 會按順序嘗試多個 loader(支持插件)。

接口 / 設(shè)計

  • Loader 協(xié)議定義:canRequestImageForURL:、requestImageWithURL:options:context:progress:completed: 等。

  • 這讓 SDWebImage 能被擴(kuò)展用于更多來源。(sdwebimage.github.io)


8) Transformers / Context / Options(變換與配置)

職責(zé)

  • Transformer:對 UIImage 做后處理(裁剪、圓角、模糊、縮放等),并且可以把變換后的結(jié)果單獨(dú)緩存(通過變換 key 來分隔)。

  • Options/Context:運(yùn)行時可傳的字典,用于配置解碼選項、優(yōu)先級、是否只從緩存讀取、緩存策略、自定義 cache key、transition 配置等。

實(shí)現(xiàn)要點(diǎn)

  • Transformer 需要提供一個唯一 key,作為緩存二次區(qū)分(即同 URL + 不同 transform 視為不同緩存條目)。

  • Context 支持傳入 coder、transformer、imageScaleFactor、animatedImageClass 等。(sdwebimage.github.io)


9) SDWebImageTransition(過渡動畫)

職責(zé)

  • 為圖像替換提供內(nèi)置/可定制的過渡,像淡入、交叉溶解等。過渡僅在主線程執(zhí)行并與 UIImageView 的 setImage 結(jié)合。

關(guān)鍵點(diǎn)

  • 過渡可以是自定義的動畫 block,允許在 image 設(shè)置前后做動畫。(sdwebimage.github.io)

10) 輔助類 / 工具類(若干)

  • SDWebImageDownloaderOperation(上面已講)

  • SDMemoryCache / SDImageCache 的內(nèi)部 helper(比如 metadata 讀寫、文件名 MD5 計算)

  • SDImageCacheKeyFilter、SDImageCacheSerializer(接口,用于自定義 key / 磁盤序列化)

  • SDWebImagePrefetcher(預(yù)取器:批量下載并緩存)

  • SDWebImagePlayAnimatedImage / AnimatedImageView(支持多幀動畫展示的視圖或插件)


常見源碼熱點(diǎn)(你打開文件時應(yīng)該優(yōu)先看的地方)

  1. UIImageView+WebCache.m:看請求如何構(gòu)造,如何取消,以及如何包裝 completion。

  2. SDWebImageManager.m:核心流程(memory → disk → loader),和任務(wù)合并邏輯。

  3. SDImageCache.m:磁盤寫入策略、key 生成、LRU 清理策略。

  4. SDWebImageDownloader.m & SDWebImageDownloaderOperation.m:并發(fā)/合并/取消/HTTP 緩存處理。

  5. SDImageCodersManager.m / SDImageIOCoder.m:解碼實(shí)現(xiàn)(注意異步解碼線程池)。

  6. SDImageLoadersManager + 各種 loader(Photos、APNG、Data 等)。

這些文件通常位于倉庫 SDWebImageSDWebImage 子目錄下;插件如 WebP 在 SDWebImageWebPCoder 倉庫中。(GitHub)


對源碼閱讀的建議(實(shí)踐技巧)

  • 先從 UIImageView+WebCache 跟進(jìn)調(diào)用鏈到 SDWebImageManager,把一個完整請求的生命周期走一遍(在斷點(diǎn)或打印下)。

  • 關(guān)注 CombinedOperation 的 cancel:重用環(huán)境(表格/集合視圖)里這是最容易出問題的地方。

  • 找到磁盤緩存的寫入點(diǎn),查看它如何處理原始 NSData 與編碼后的數(shù)據(jù)(這決定磁盤緩存的大小與 IO)。

  • 看解碼是在哪個隊列做的(ImageIO / custom coder),測量主線程是否會被解碼占用。

  • 如果你關(guān)心新格式(WebP/AVIF),查看對應(yīng) plugin 的 coder 源碼(通常是獨(dú)立倉庫)。(GitHub)


小結(jié)(回顧要點(diǎn))

  • SDWebImage 的源碼是典型的職責(zé)分離 + 插件化:Manager、Cache、Downloader、Coder、Loader、Transformer 等模塊都相對獨(dú)立,可替換/擴(kuò)展。(sdwebimage.github.io)

  • 性能關(guān)鍵點(diǎn)在于:內(nèi)存緩存(解碼后位圖)+ 磁盤緩存(避免重復(fù)下載)+ 異步解碼(避免主線程卡頓)+ 下載去重與并發(fā)控制。(GitHub)


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容