当前位置: 首页 > news >正文

SDWebImage源码学习

SDWebImage源码学习

文章目录

  • SDWebImage源码学习
    • 前言
    • 宏定义
    • SDWebImageOptions
      • 1. 核心选项
      • 2. 缓存策略
      • 3. 安全与后台
      • 4. 优先级与占位图
      • 5. 图片处理
      • 6. 高级查询
      • 7. 过渡与动画
      • 8. 其他选项
    • SDWebImageContext
    • SDOperationsDictionary
    • 入口函数
      • 1. 操作唯一性与冲突避免
      • 2. 线程安全与生命周期管理
      • 3. 操作键(Operation Key)机制
      • 4. 与 SDWebImage 核心模块协作
    • 工具层
      • 缓存查找
        • SDImageCacheConfig
        • SDImageCacheType
        • processedResultForURL
        • callCacheProcessForOperation
          • cacheKeyForURL 和 originalCacheKeyForURL
        • SDImageCache
          • 初始化方法
      • queryCacheOperationForKey
      • 图像下载
          • callDownloadProcessForOperation
        • downloadImageWithURL
        • start
    • 总结流程

前言

SDWebImage的具体流程图
img

宏定义

#ifndef dispatch_main_async_safe
#define dispatch_main_async_safe(block)\if (dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) == dispatch_queue_get_label(dispatch_get_main_queue())) {\block();\} else {\dispatch_async(dispatch_get_main_queue(), block);\}//弃用声明
#pragma clang deprecated(dispatch_main_async_safe, "Use SDCallbackQueue instead")
#endif

就是将block的回调放在了主线程之中,避免了线程错误,在新的版本之中我们使用SDCallbackQueue进行调用

// 旧方案(已弃用)
dispatch_main_async_safe(^{self.image = image;
});// 新方案(推荐)
SDCallbackQueue *mainQueue = [SDCallbackQueue mainQueue];
[mainQueue async:^{self.image = image;
}];

SDWebImageOptions

SDWebImageOptions 是 SDWebImage 框架中用于 配置图片加载与缓存策略 的核心枚举类型,通过位掩码组合多种选项,控制图片下载、缓存、解码等行为的细节。

1. 核心选项

1.1 SDWebImageRetryFailed

  • 作用:默认情况下,下载失败的 URL 会被加入黑名单,后续不再重试。启用此选项后,即使下载失败仍会尝试重新下载。
  • 适用场景:需多次重试的临时网络错误场景。

1.2 SDWebImageLowPriority

  • 作用:禁止在 UI 交互(如 UIScrollView 滚动)时下载图片,改为在滚动减速时再启动下载。
  • 适用场景:优化列表滚动时的性能。

1.3 SDWebImageProgressiveLoad

  • 作用:启用渐进式加载,图片边下载边逐行显示(类似浏览器)。
  • 适用场景:大图加载时提升用户体验。

2. 缓存策略

2.1 SDWebImageRefreshCached

  • 作用:即使本地有缓存,仍根据 HTTP 缓存策略重新下载图片(使用 NSURLCache 而非 SDWebImage 的磁盘缓存)。
  • 适用场景:URL 不变但图片内容频繁更新的场景(如用户头像)。

** SDWebImageFromCacheOnly**

  • 作用:仅从缓存加载图片,不触发网络请求。
  • 适用场景:离线模式或强制使用缓存时。

2.3 SDWebImageFromLoaderOnly

  • 作用:仅从网络加载图片,忽略缓存。
  • 适用场景:需强制刷新图片的场景。

3. 安全与后台

3.1 SDWebImageHandleCookies

  • 作用:处理 NSHTTPCookieStore 中的 Cookies,通过设置 NSMutableURLRequest.HTTPShouldHandleCookies = YES 实现。
  • 适用场景:需要维护会话状态的请求。

3.2 SDWebImageAllowInvalidSSLCertificates

  • 作用:允许不受信任的 SSL 证书。
  • 适用场景:测试环境调试,生产环境慎用。

3.3 SDWebImageContinueInBackground

  • 作用:应用进入后台后继续下载(需系统支持后台任务)。
  • 适用场景:长时间下载大图的场景。

4. 优先级与占位图

4.1 SDWebImageHighPriority

  • 作用:将下载任务提升至队列最前端,立即执行。
  • 适用场景:关键图片的优先加载。

4.2 SDWebImageDelayPlaceholder

  • 作用:延迟显示占位图,直到图片下载完成。
  • 适用场景:避免占位图与加载完成图切换时的闪烁。

5. 图片处理

5.1 SDWebImageTransformAnimatedImage

  • 作用:允许对动画图片(如 GIF)应用变换操作(如裁剪、滤镜)。
  • 适用场景:需对动图进行自定义处理的场景。

5.2 SDWebImageAvoidAutoSetImage

  • 作用:阻止自动设置图片到视图,需在 completedBlock 中手动处理。
  • 适用场景:需自定义动画(如淡入淡出)或后处理的场景。

5.3 SDWebImageScaleDownLargeImages

  • 作用:自动缩小大图以适应设备内存限制(默认限制 60MB)。
  • 适用场景:内存敏感型应用,防止 OOM。

6. 高级查询

6.1 SDWebImageQueryMemoryData

  • 作用:强制查询内存中的图片数据(默认仅缓存 UIImage 对象)。
  • 适用场景:需直接操作原始图片数据的场景。

6.2 SDWebImageQueryMemoryDataSync / SDWebImageQueryDiskDataSync

  • 作用:同步查询内存或磁盘数据(默认异步)。
  • 适用场景:需避免单元格重用闪烁的场景(慎用,可能阻塞主线程)。

7. 过渡与动画

7.1 SDWebImageForceTransition

  • 作用:强制应用 SDWebImageTransition 过渡动画到缓存图片(默认仅网络下载触发)。
  • 适用场景:统一缓存与网络图片的展示效果。

7.2 SDWebImageDecodeFirstFrameOnly

  • 作用:仅解码动图的首帧,生成静态图片。
  • 适用场景:仅需显示动图首帧的场景。

7.3 SDWebImagePreloadAllFrames

  • 作用:预加载动图所有帧到内存(减少渲染时的 CPU 开销)。
  • 适用场景:多个视图共享同一动图的场景。

8. 其他选项

8.1 SDWebImageAvoidAutoCancelImage

  • 作用:禁用自动取消同一视图的旧图片加载请求。
  • 适用场景:需同时加载多张图片到同一视图的不同区域。

8.2 SDWebImageWaitStoreCache

  • 作用:等待图片数据完全写入磁盘后再回调。
  • 适用场景:需确保缓存写入完成的后续操作。

SDWebImageContext

键(Key)值类型功能与作用场景
SDWebImageContextSetImageOperationKeyNSString操作唯一性控制:唯一标识加载任务,解决同一视图(如 UIButton 不同状态)多次加载时的任务冲突。默认使用视图类名生成唯一键。
SDWebImageContextImageCacheid<SDImageCache>缓存实例替换:使用自定义缓存实例(如分业务隔离缓存),覆盖全局默认的 SDImageCache.sharedImageCache
SDWebImageContextImageLoaderid<SDImageLoader>下载器替换:自定义网络请求逻辑(如加密传输、代理配置),替代默认的 SDWebImageDownloader
SDWebImageContextImageDecodeOptionsSDImageCoderOptions解码优化:传递缩略图尺寸(SDImageCoderDecodeThumbnailPixelSize)或渐进式解码参数,减少内存占用并提升渲染性能。
SDWebImageContextCallbackQueueSDCallbackQueue线程安全控制:指定回调执行队列(如主队列或串行队列),确保 UI 更新在主线程执行。
SDWebImageContextStoreCacheTypeSDImageCacheType缓存策略覆盖:强制指定存储方式(内存/磁盘/不存储),优先级高于全局配置。例如敏感图片仅缓存在内存中。
SDWebImageContextImageTransformerid<SDImageTransformer>图片变换:加载完成后自动应用变换(如圆角裁剪、滤镜),变换后的图片会独立缓存。需实现 SDImageTransformer 协议。
SDWebImageContextTransitionSDWebImageTransition过渡动画:定义图片加载完成后的动画效果(如淡入、缩放),增强用户体验。
SDWebImageContextDownloadRequestModifierid<SDWebImageDownloaderRequestModifier>请求修改:动态修改下载请求,例如添加 HTTP 头、调整超时时间或重定向策略。

SDOperationsDictionary

typedef NSMapTable<NSString *, id<SDWebImageOperation>> SDOperationsDictionary;

SDOperationsDictionary继承于NSMapTab,当视图发起新的图片加载请求时,SDOperationsDictionary会通过唯一标识符(如 validOperationKey)查找并取消当前视图关联的未完成操作,

维度NSDictionaryNSMapTable
Key 类型必须实现 NSCopying 的 Objective-C 对象任意指针(包括非对象)
内存管理Key 拷贝 + Value 强引用可配置强/弱引用或拷贝行为
适用场景静态键值存储、高频读取弱引用缓存、非标准键值映射
线程安全
哈希冲突处理链表法可定制(依赖初始化参数)

入口函数

image-20250517222824420

我们看到,无论如何调用哪一个函数,最终都会进入

- (void)sd_setImageWithURL:(nullable NSURL *)urlplaceholderImage:(nullable UIImage *)placeholderoptions:(SDWebImageOptions)optionsprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDExternalCompletionBlock)completedBlock;

进入原函数看一下

- (nullable id<SDWebImageOperation>)sd_internalSetImageWithURL:(nullable NSURL *)urlplaceholderImage:(nullable UIImage *)placeholderoptions:(SDWebImageOptions)optionscontext:(nullable SDWebImageContext *)contextsetImageBlock:(nullable SDSetImageBlock)setImageBlockprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nullable SDInternalCompletionBlock)completedBlock {// Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, Xcode won't// throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.//  if url is NSString and shouldUseWeakMemoryCache is true, [cacheKeyForURL:context] will crash. just for a  global protect.//支持NSString类型if ([url isKindOfClass:NSString.class]) {url = [NSURL URLWithString:(NSString *)url];}// Prevents app crashing on argument type error like sending NSNull instead of NSURLif (![url isKindOfClass:NSURL.class]) {url = nil;}/if (context) {//复制防止可变对象 context = [context copy];} else {context = [NSDictionary dictionary];}//每个视图(如 UIImageView、UIButton)的图片加载操作通过 validOperationKey 唯一标识。例如,同一视图中可能同时加载背景图与图标(即不同的state),通过不同键值区分任务。                                     NSString *validOperationKey = context[SDWebImageContextSetImageOperationKey];if (!validOperationKey) {//如果上下文传入时,没有制定SDWebImageContextSetImageOperationKey之中的内容,SDWebImage 将使用视图类名(如 NSStringFromClass([self class]))作为默认键值validOperationKey = NSStringFromClass([self class]);SDWebImageMutableContext *mutableContext = [context mutableCopy];mutableContext[SDWebImageContextSetImageOperationKey] = validOperationKey;context = [mutableContext copy];}self.sd_latestOperationKey = validOperationKey;//当启动新的图片加载请求时,若未显式指定 SDWebImageAvoidAutoCancelImage 选项,自动取消同一视图实例的旧图片加载任务if (!(SD_OPTIONS_CONTAINS(options, SDWebImageAvoidAutoCancelImage))) {//之中的队列任务是通过SDOperationsDictionary,这个SDOperationsDictionary继承于NSMapTable,NSMapTable[self sd_cancelImageLoadOperationWithKey:validOperationKey];}//通过 SDWebImageLoadState 对象记录当前视图的图片加载状态(如请求 URL、进度、错误信息等)SDWebImageLoadState *loadState = [self sd_imageLoadStateForKey:validOperationKey];                         if (!loadState) {loadState = [SDWebImageLoadState new];}loadState.url = url;[self sd_setImageLoadState:loadState forKey:validOperationKey];//分配managerSDWebImageManager *manager = context[SDWebImageContextCustomManager];if (!manager) {manager = [SDWebImageManager sharedManager];} else {// 删除manager避免循环引用 (manger -> loader -> operation -> context -> manager)SDWebImageMutableContext *mutableContext = [context mutableCopy];mutableContext[SDWebImageContextCustomManager] = nil;context = [mutableContext copy];}//获取回调队列SDCallbackQueue *queue = context[SDWebImageContextCallbackQueue];BOOL shouldUseWeakCache = NO;if ([manager.imageCache isKindOfClass:SDImageCache.class]) {shouldUseWeakCache = ((SDImageCache *)manager.imageCache).config.shouldUseWeakMemoryCache;}if (!(options & SDWebImageDelayPlaceholder)) {if (shouldUseWeakCache) {NSString *key = [manager cacheKeyForURL:url context:context];// call memory cache to trigger weak cache sync logic, ignore the return value and go on normal query// this unfortunately will cause twice memory cache query, but it's fast enough// in the future the weak cache feature may be re-design or removed[((SDImageCache *)manager.imageCache) imageFromMemoryCacheForKey:key];}//如果没有获取到指定队列,就在主队列之中运行,加载placeholder[(queue ?: SDCallbackQueue.mainQueue) async:^{[self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];}];}//SDWebImageOperation 是继承于NSOperation,支持取消功能的任务队列id <SDWebImageOperation> operation = nil;if (url) {// 重置下载进程NSProgress *imageProgress = loadState.progress;if (imageProgress) {imageProgress.totalUnitCount = 0;imageProgress.completedUnitCount = 0;}#if SD_UIKIT || SD_MAC// 检查是否有指示器并启用[self sd_startImageIndicatorWithQueue:queue];id<SDWebImageIndicator> imageIndicator = self.sd_imageIndicator;
#endifSDImageLoaderProgressBlock combinedProgressBlock = ^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {if (imageProgress) {imageProgress.totalUnitCount = expectedSize;imageProgress.completedUnitCount = receivedSize;}
#if SD_UIKIT || SD_MACif ([imageIndicator respondsToSelector:@selector(updateIndicatorProgress:)]) {double progress = 0;if (expectedSize != 0) {progress = (double)receivedSize / expectedSize;}progress = MAX(MIN(progress, 1), 0); // 0.0 - 1.0dispatch_async(dispatch_get_main_queue(), ^{[imageIndicator updateIndicatorProgress:progress];});}
#endifif (progressBlock) {progressBlock(receivedSize, expectedSize, targetURL);}};//通过 @weakify/@strongify 宏避免 Block 强引用导致的内存泄漏,加载图片@weakify(self);operation = [manager loadImageWithURL:url options:options context:context progress:combinedProgressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {@strongify(self);if (!self) { return; }// if the progress not been updated, mark it to complete stateif (imageProgress && finished && !error && imageProgress.totalUnitCount == 0 && imageProgress.completedUnitCount == 0) {imageProgress.totalUnitCount = SDWebImageProgressUnitCountUnknown;imageProgress.completedUnitCount = SDWebImageProgressUnitCountUnknown;}#if SD_UIKIT || SD_MAC// check and stop image indicatorif (finished) {[self sd_stopImageIndicatorWithQueue:queue];}
#endif//根据选项控制是否自动设置图片或延迟占位符。BOOL shouldCallCompletedBlock = finished || (options & SDWebImageAvoidAutoSetImage);BOOL shouldNotSetImage = ((image && (options & SDWebImageAvoidAutoSetImage)) ||(!image && !(options & SDWebImageDelayPlaceholder)));SDWebImageNoParamsBlock callCompletedBlockClosure = ^{if (!self) { return; }if (!shouldNotSetImage) {[self sd_setNeedsLayout];}if (completedBlock && shouldCallCompletedBlock) {completedBlock(image, data, error, cacheType, finished, url);}};// case 1a: we got an image, but the SDWebImageAvoidAutoSetImage flag is set// OR// case 1b: we got no image and the SDWebImageDelayPlaceholder is not setif (shouldNotSetImage) {[(queue ?: SDCallbackQueue.mainQueue) async:callCompletedBlockClosure];return;}UIImage *targetImage = nil;NSData *targetData = nil;if (image) {// case 2a: we got an image and the SDWebImageAvoidAutoSetImage is not settargetImage = image;targetData = data;} else if (options & SDWebImageDelayPlaceholder) {// case 2b: we got no image and the SDWebImageDelayPlaceholder flag is settargetImage = placeholder;targetData = nil;}#if SD_UIKIT || SD_MAC// 确定最终显示的图像(网络图片/占位符)并应用过渡动画。SDWebImageTransition *transition = nil;BOOL shouldUseTransition = NO;if (options & SDWebImageForceTransition) {// AlwaysshouldUseTransition = YES;} else if (cacheType == SDImageCacheTypeNone) {// From networkshouldUseTransition = YES;} else {// From disk (and, user don't use sync query)if (cacheType == SDImageCacheTypeMemory) {shouldUseTransition = NO;} else if (cacheType == SDImageCacheTypeDisk) {if (options & SDWebImageQueryMemoryDataSync || options & SDWebImageQueryDiskDataSync) {shouldUseTransition = NO;} else {shouldUseTransition = YES;}} else {// Not valid cache type, fallbackshouldUseTransition = NO;}}if (finished && shouldUseTransition) {transition = self.sd_imageTransition;}
#endif[(queue ?: SDCallbackQueue.mainQueue) async:^{
#if SD_UIKIT || SD_MAC[self sd_setImage:targetImage imageData:targetData options:options basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL callback:callCompletedBlockClosure];
#else[self sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:cacheType imageURL:imageURL];callCompletedBlockClosure();
#endif}];}];//加入任务队列[self sd_setImageLoadOperation:operation forKey:validOperationKey];} else {
#if SD_UIKIT || SD_MAC//url不存在[self sd_stopImageIndicatorWithQueue:queue];
#endifif (completedBlock) {//返回错误信息[(queue ?: SDCallbackQueue.mainQueue) async:^{NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidURL userInfo:@{NSLocalizedDescriptionKey : @"Image url is nil"}];completedBlock(nil, nil, error, SDImageCacheTypeNone, YES, url);}];}}return operation;
}

SDOperationsDictionary(在 SDWebImage 中实际名为 operationDictionary)是用于 管理视图(如 UIImageViewUIButton)关联的图片加载操作的核心数据结构。它的作用主要体现在以下几个方面:


1. 操作唯一性与冲突避免

  • 取消旧任务:当视图发起新的图片加载请求时,operationDictionary 会通过唯一标识符(如 validOperationKey)查找并取消当前视图关联的未完成操作。例如:

    [self sd_cancelImageLoadOperationWithKey:validOperationKey];
    

    这确保同一视图不会因重复请求(如快速滑动列表时)导致图片错位或资源浪费。

  • 多操作共存:支持不同操作键区分同一视图的多个独立任务(如同时加载头像和背景图)。


2. 线程安全与生命周期管理

  • 线程安全存储:通过关联对象(Associated Object)动态挂载到 UIView 上,使用 NSMapTableNSMutableDictionary 存储操作,确保多线程环境下操作增删的安全性。
  • 自动释放机制:当视图释放时,关联的 operationDictionary 会自动清理未完成的操作,避免内存泄漏。

3. 操作键(Operation Key)机制

  • 灵活标识符:默认以视图类名(如 UIImageView)作为操作键,但也支持通过上下文(SDWebImageContext)自定义键,适应复杂场景。
  • 动态绑定:例如,同一 UIImageView 的不同加载任务(如普通状态与高亮状态)可通过不同键独立管理。

4. 与 SDWebImage 核心模块协作

  • 协调下载与缓存:通过 operationDictionary 管理 SDWebImageOperation 对象(如下载任务 SDWebImageDownloaderOperation),实现与 SDWebImageManagerSDImageCache 的交互。
  • 支持优先级控制:例如,在 UITableView 滑动时,通过取消低优先级任务优化性能。

工具层

从上面UIKit+WebCache暴露的接口函数来看,我们会发现图片的加载核心其实是loadImageWithURL这个load函数,我们可以看到这个函数是在SDWebImageManager之中

- (SDWebImageCombinedOperation *)loadImageWithURL:(nullable NSURL *)urloptions:(SDWebImageOptions)optionscontext:(nullable SDWebImageContext *)contextprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nonnull SDInternalCompletionBlock)completedBlock {// Invoking this method without a completedBlock is pointlessNSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");// 这里我们通过允许将 URL 作为 NSString 传递来防止这类错误if ([url isKindOfClass:NSString.class]) {url = [NSURL URLWithString:(NSString *)url];}// 防止因参数类型错误(如传递 NSNull 而非 NSURL)导致应用崩溃if (![url isKindOfClass:NSURL.class]) {url = nil;}SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];operation.manager = self;
//两个信号量分别控制操作failedURLs和runningOperations的线程安全。信号量为一,实现锁的效果BOOL isFailedUrl = NO;if (url) {SD_LOCK(_failedURLsLock);isFailedUrl = [self.failedURLs containsObject:url];SD_UNLOCK(_failedURLsLock);}// 预处理 options 和 context 参数,为管理器决定最终结果SDWebImageOptionsResult *result = [self processedResultForURL:url options:options context:context];
//url的绝对字符串长度为0或者(在不禁用黑名单的情况下)并且Url在黑名单内,发出警告提示if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {NSString *description = isFailedUrl ? @"Image url is blacklisted" : @"Image url is nil";NSInteger code = isFailedUrl ? SDWebImageErrorBlackListed : SDWebImageErrorInvalidURL;//调用completionBlock块,结束该函数[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:code userInfo:@{NSLocalizedDescriptionKey : description}] queue:result.context[SDWebImageContextCallbackQueue] url:url];return operation;}//将这个操作加入进程SD_LOCK(_runningOperationsLock);[self.runningOperations addObject:operation];SD_UNLOCK(_runningOperationsLock);// 开始从缓存加载图片的入口,主要步骤如下:// 无转换器的步骤:// 1. 从缓存查询图片,未命中// 2. 下载数据并生成图片// 3. 将图片存储至缓存// 带转换器的步骤:// 1. 从缓存查询转换后的图片,未命中// 2. 从缓存查询原始图片,未命中// 3. 下载数据并生成图片// 4. 在 CPU 中执行转换// 5. 将原始图片存储至缓存// 6. 将转换后的图片存储至缓存[self callCacheProcessForOperation:operation url:url options:result.options context:result.context progress:progressBlock completed:completedBlock];return operation;
}

SDWebImageManager是一个单例,作用就是调度SDImageCache和SDWebImageDownloader进行缓存和下载操作的。

我们的程序为了避免在查看缓存和下载两个部分代码进行耦合,所用使用的SDWebImageCombinedOperation将缓存与下载的协作逻辑内聚到单一对象中,提升代码可维护性。

缓存查找

我们在loadImageWithURL可以看到,最后程序进入了[self callCacheProcessForOperation:operation url:url options:result.options context:result.context progress:progressBlock completed:completedBlock];的方法之中,看名字就能大概知道,这个函数就是用来查找缓存的。

SDImageCacheConfig

用于存取缓存配置

static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week@implementation SDImageCacheConfig- (instancetype)init {if (self = [super init]) {_shouldDecompressImages = YES;_shouldDisableiCloud = YES;_shouldCacheImagesInMemory = YES;_shouldUseWeakMemoryCache = YES;_diskCacheReadingOptions = 0;_diskCacheWritingOptions = NSDataWritingAtomic;_maxCacheAge = kDefaultCacheMaxCacheAge;_maxCacheSize = 0;_diskCacheExpireType = SDImageCacheConfigExpireTypeModificationDate;}return self;
}
SDImageCacheType
typedef NS_ENUM(NSInteger, SDImageCacheType) {//从网上下载的SDImageCacheTypeNone,//从磁盘中获取的SDImageCacheTypeDisk,//从内存中获取的SDImageCacheTypeMemory,SDImageCacheTypeAll
};
processedResultForURL
- (SDWebImageOptionsResult *)processedResultForURL:(NSURL *)url options:(SDWebImageOptions)options context:(SDWebImageContext *)context {// 初始化结果对象和可变上下文SDWebImageOptionsResult *result;SDWebImageMutableContext *mutableContext = [SDWebImageMutableContext dictionary];// 1. 补充默认的图片转换器if (!context[SDWebImageContextImageTransformer]) {id<SDImageTransformer> transformer = self.transformer;[mutableContext setValue:transformer forKey:SDWebImageContextImageTransformer];}// 2. 补充默认的缓存键生成规则if (!context[SDWebImageContextCacheKeyFilter]) {id<SDWebImageCacheKeyFilter> cacheKeyFilter = self.cacheKeyFilter;[mutableContext setValue:cacheKeyFilter forKey:SDWebImageContextCacheKeyFilter];}// 3. 补充默认的缓存序列化器if (!context[SDWebImageContextCacheSerializer]) {id<SDWebImageCacheSerializer> cacheSerializer = self.cacheSerializer;[mutableContext setValue:cacheSerializer forKey:SDWebImageContextCacheSerializer];}// 4. 合并用户自定义上下文与默认配置if (mutableContext.count > 0) {if (context) {[mutableContext addEntriesFromDictionary:context]; // 保留用户自定义优先级}context = [mutableContext copy]; // 生成最终不可变上下文}// 5. 应用参数处理器(动态修改选项或上下文)if (self.optionsProcessor) {result = [self.optionsProcessor processedResultForURL:url options:options context:context];}// 6. 若无自定义处理器,生成默认结果if (!result) {result = [[SDWebImageOptionsResult alloc] initWithOptions:options context:context];}return result;
}

生成的 result 对象用于后续 callCacheProcessForOperation: 缓存查询和 callDownloadProcessForOperation: 下载流程。

callCacheProcessForOperation
// 查询普通缓存流程
- (void)callCacheProcessForOperation:(nonnull SDWebImageCombinedOperation *)operationurl:(nonnull NSURL *)urloptions:(SDWebImageOptions)optionscontext:(nullable SDWebImageContext *)contextprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nullable SDInternalCompletionBlock)completedBlock {// 获取要使用的图片缓存实例id<SDImageCache> imageCache = context[SDWebImageContextImageCache];if (!imageCache) {imageCache = self.imageCache;  // 若未指定则使用默认缓存}// 获取查询缓存类型(默认查询所有缓存类型)SDImageCacheType queryCacheType = SDImageCacheTypeAll;if (context[SDWebImageContextQueryCacheType]) {queryCacheType = [context[SDWebImageContextQueryCacheType] integerValue];}// 检查是否需要查询缓存(若未设置 SDWebImageFromLoaderOnly 选项则需查询)BOOL shouldQueryCache = !SD_OPTIONS_CONTAINS(options, SDWebImageFromLoaderOnly);if (shouldQueryCache) {// 生成转换后的缓存键(避免缩略图参数影响缓存键生成)NSString *key = [self cacheKeyForURL:url context:context];// 剥离缩略图相关参数,防止缓存键不匹配SDWebImageMutableContext *mutableContext = [context mutableCopy];mutableContext[SDWebImageContextImageThumbnailPixelSize] = nil;    // 清除缩略图尺寸mutableContext[SDWebImageContextImagePreserveAspectRatio] = nil;    // 清除宽高比保留标记@weakify(operation);  // 弱引用操作对象防止循环引用operation.cacheOperation = [imageCache queryImageForKey:key options:options context:mutableContext cacheType:queryCacheType completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) {@strongify(operation);  // 强引用操作对象确保执行期间不被释放if (!operation || operation.isCancelled) {// 操作被用户取消:触发取消错误回调并清理资源[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"用户在查询缓存期间取消了操作"}] queue:context[SDWebImageContextCallbackQueue] url:url];[self safelyRemoveOperationFromRunning:operation];return;} else if (!cachedImage) {// 未命中转换后的缓存键,尝试查询原始缓存键NSString *originKey = [self originalCacheKeyForURL:url context:context];BOOL mayInOriginalCache = ![key isEqualToString:originKey];  // 判断是否可能存在于原始缓存if (mayInOriginalCache) {// 进入原始缓存查询流程(如下载后应用转换)[self callOriginalCacheProcessForOperation:operation url:url options:options context:context progress:progressBlock completed:completedBlock];return;}}// 无论是否命中缓存,进入下载流程[self callDownloadProcessForOperation:operation url:url options:options context:context cachedImage:cachedImage cachedData:cachedData cacheType:cacheType progress:progressBlock completed:completedBlock];}];} else {// 跳过缓存查询,直接进入下载流程[self callDownloadProcessForOperation:operation url:url options:options context:context cachedImage:nil cachedData:nil cacheType:SDImageCacheTypeNone progress:progressBlock completed:completedBlock];}
}
cacheKeyForURL 和 originalCacheKeyForURL

在 SDWebImage 的缓存流程中,cacheKeyForURLoriginalCacheKeyForURL 生成的缓存键有以下核心区别:

  • cacheKeyForURL
    用于生成 转换后的缓存键,通常包含图片的 处理参数(如缩略图尺寸、裁剪模式、压缩质量等)。例如,若 URL 中包含 thumbnail=300x300quality=80 等参数,这些参数会影响最终生成的缓存键。
    • 目的:确保不同处理参数的图片在缓存中独立存储,避免显示错误(如不同尺寸的缩略图混用)。
// 示例:包含缩略图尺寸参数的键生成
NSString *key = [SDWebImageManager.sharedManager cacheKeyForURL:url context:@{SDWebImageContextImageThumbnailPixelSize: @(CGSizeMake(300, 300))}];
  • originalCacheKeyForURL
    生成 原始缓存键剥离所有图片处理参数,仅保留 URL 的 核心标识(如基础路径、固定参数)。例如,即使 URL 包含 thumbnail=300x300,原始缓存键会忽略该参数,仅基于原图 URL 生成键值。
    • 目的:支持从原始图片本地裁剪或转换,避免重复下载未处理的原图。
// 示例:剥离转换参数后的键生成
SDWebImageMutableContext *mutableContext = [context mutableCopy];
mutableContext[SDWebImageContextImageThumbnailPixelSize] = nil;
NSString *originKey = [self originalCacheKeyForURL:url context:mutableContext];

通过这种设计,SDWebImage 实现了 高效缓存复用灵活参数控制 的平衡。

SDImageCache

SDImageCache 是 SDWebImage 框架中 专门负责图片缓存管理的核心组件,其核心功能与设计模式如下:

  1. 自动缓存管理

    • 过期策略:默认清理超过一周(maxCacheAge 默认 7 天)的缓存文件。
    • 容量控制:可设置最大缓存大小(maxCacheSize),超出时按 LRU(最近最少使用)策略清理。
    • 后台清理:通过监听 UIApplicationDidEnterBackgroundNotification 等系统事件,在后台线程异步清理磁盘缓存。
  2. 线程安全与性能优化

    • 使用 dispatch_semaphore 控制对 NSMapTable 弱引用缓存的线程安全访问。
    • 图片解码优化:通过 SDWebImageDecoder 提前解压图片数据,避免主线程重复解码造成的性能损耗。
  3. 默认单例模式

    • 全局访问入口:通过 +[SDImageCache sharedImageCache] 获取单例实例,使用 dispatch_once 确保线程安全初始化。

    • 统一资源管理:单例模式便于集中控制内存和磁盘缓存,避免多实例导致的资源重复或状态不一致。

  4. 支持多实例场景

    • 命名空间隔离:可通过 initWithNamespace:diskCacheDirectory: 创建独立命名空间的缓存实例,实现不同内容的缓存隔离(如用户头像与商品图片分开存储)。

    • 灵活配置:每个实例可单独设置 maxCacheAgemaxCacheSize 等参数,适应不同缓存策略需求。

初始化方法

看一下初始化方法,理解

- (nonnull instancetype)initWithNamespace:(nonnull NSString *)nsdiskCacheDirectory:(nullable NSString *)directoryconfig:(nullable SDImageCacheConfig *)config {if ((self = [super init])) {// 强校验:命名空间不能为空(用于缓存目录隔离)NSAssert(ns, @"缓存命名空间不能为空");// 配置对象处理(若未传入则使用默认配置)if (!config) {config = SDImageCacheConfig.defaultCacheConfig;}_config = [config copy];  // 深拷贝配置防止外部修改// 创建磁盘IO队列(串行队列保证线程安全),所有磁盘操作(读/写/删)在此队列执行以保证线程安全// 队列属性继承自config(可自定义优先级等属性)dispatch_queue_attr_t ioQueueAttributes = _config.ioQueueAttributes;_ioQueue = dispatch_queue_create("com.hackemist.SDImageCache.ioQueue", ioQueueAttributes);// 断言保证队列创建成功(防止错误的队列属性配置)NSAssert(_ioQueue, @"IO队列创建失败,请检查ioQueueAttributes配置");// 初始化内存缓存(使用配置指定的缓存类)// 自定义内存缓存类必须实现SDMemoryCache协议(如支持NSCache扩展)NSAssert([config.memoryCacheClass conformsToProtocol:@protocol(SDMemoryCache)],@"自定义内存缓存类必须遵循SDMemoryCache协议");_memoryCache = [[config.memoryCacheClass alloc] initWithConfig:_config];  // 注入配置// 处理磁盘缓存路径(三级目录结构)if (!directory) {// 默认路径:沙盒/Library/Caches/com.hackemist.SDWebImageCachedirectory = [self.class defaultDiskCacheDirectory];}// 拼接命名空间子目录(如default/avatar等业务隔离)_diskCachePath = [directory stringByAppendingPathComponent:ns];// 初始化磁盘缓存(支持自定义磁盘缓存实现)// 自定义类必须实现SDDiskCache协议(如加密磁盘存储)NSAssert([config.diskCacheClass conformsToProtocol:@protocol(SDDiskCache)],@"自定义磁盘缓存类必须遵循SDDiskCache协议");_diskCache = [[config.diskCacheClass alloc] initWithCachePath:_diskCachePath config:_config];// 执行缓存目录迁移(兼容旧版本路径结构)// 例如从无扩展名文件迁移到带扩展名版本[self migrateDiskCacheDirectory];  // 详见网页1的兼容处理#if SD_UIKIT// 注册iOS应用生命周期通知// 1. 应用终止时:持久化缓存元数据(如过期时间)[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(applicationWillTerminate:)name:UIApplicationWillTerminateNotificationobject:nil];// 2. 进入后台时:触发异步清理过期缓存[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(applicationDidEnterBackground:)name:UIApplicationDidEnterBackgroundNotificationobject:nil];
#endif
#if SD_MAC// macOS平台的特殊处理(使用NSApplication生命周期)[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(applicationWillTerminate:)name:NSApplicationWillTerminateNotificationobject:nil];
#endif}return self;
}

queryCacheOperationForKey

经过一系列调用 ,我们最终会调用到

- (nullable SDImageCacheToken *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context cacheType:(SDImageCacheType)queryCacheType done:(nullable SDImageCacheQueryCompletionBlock)doneBlock {if (!key) {if (doneBlock) {doneBlock(nil, nil, SDImageCacheTypeNone);  // 键为空时直接返回无缓存}return nil;}// 无效缓存类型if (queryCacheType == SDImageCacheTypeNone) {if (doneBlock) {doneBlock(nil, nil, SDImageCacheTypeNone);  // 指定不查询任何缓存时直接返回}return nil;}// 首先检查内存缓存...UIImage *image;BOOL shouldQueryDiskOnly = (queryCacheType == SDImageCacheTypeDisk);  // 是否仅查询磁盘if (!shouldQueryDiskOnly) {image = [self imageFromMemoryCacheForKey:key];  // 从内存缓存获取图片}if (image) {// 处理动态图首帧静态化或类匹配逻辑if (options & SDImageCacheDecodeFirstFrameOnly) {// 确保静态图:若为动态图则提取首帧if (image.sd_imageFrameCount > 1) {image = [[UIImage alloc] initWithCGImage:image.CGImage ...];  // 生成静态图}} else if (options & SDImageCacheMatchAnimatedImageClass) {// 检查图片类是否匹配上下文要求(如仅允许WebP动画)Class desiredImageClass = context[SDWebImageContextAnimatedImageClass];if (desiredImageClass && ![image.class isSubclassOfClass:desiredImageClass]) {image = nil;  // 类不匹配时丢弃内存缓存}}}// 判断是否仅需查询内存缓存BOOL shouldQueryMemoryOnly = (queryCacheType == SDImageCacheTypeMemory) || (image && !(options & SDImageCacheQueryMemoryData));if (shouldQueryMemoryOnly) {if (doneBlock) {doneBlock(image, nil, SDImageCacheTypeMemory);  // 直接返回内存结果}return nil;}// 开始磁盘缓存查询...SDCallbackQueue *queue = context[SDWebImageContextCallbackQueue];  // 回调队列(默认主队列)SDImageCacheToken *operation = [[SDImageCacheToken alloc] initWithDoneBlock:doneBlock];  // 创建缓存操作对象operation.key = key;operation.callbackQueue = queue;// 判断是否需要同步查询磁盘BOOL shouldQueryDiskSync = ((image && options & SDImageCacheQueryMemoryDataSync) ||(!image && options & SDImageCacheQueryDiskDataSync));// 定义磁盘数据查询块NSData* (^queryDiskDataBlock)(void) = ^NSData* {return [self diskImageDataBySearchingAllPathsForKey:key];  // 搜索所有磁盘路径获取数据};// 定义磁盘图片解码块UIImage* (^queryDiskImageBlock)(NSData*) = ^UIImage*(NSData* diskData) {UIImage *diskImage;if (image) {diskImage = image;  // 内存已命中但需原始数据} else if (diskData) {// 内存未命中,需解码磁盘数据diskImage = [self diskImageForKey:key data:diskData options:options context:context];if (shouldCacheToMemory) {[self _syncDiskToMemoryWithImage:diskImage forKey:key];  // 同步到内存缓存}}return diskImage;};// 执行磁盘查询(同步或异步)if (shouldQueryDiskSync) {// 同步查询(阻塞当前线程)dispatch_sync(self.ioQueue, ^{NSData *diskData = queryDiskDataBlock();UIImage *diskImage = queryDiskImageBlock(diskData);doneBlock(diskImage, diskData, SDImageCacheTypeDisk);});} else {// 异步查询(避免阻塞主线程)dispatch_async(self.ioQueue, ^{NSData *diskData = queryDiskDataBlock();UIImage *diskImage = queryDiskImageBlock(diskData);// 异步回调到主队列[(queue ?: SDCallbackQueue.mainQueue) async:^{doneBlock(diskImage, diskData, SDImageCacheTypeDisk);}];});}return operation;  // 返回可取消的操作对象
}

imageFromMemoryCacheForKey先使用该方法查看缓存,再通过diskImageDataBySearchingAllPathsForKey查看缓存

diskImageDataBySearchingAllPathsForKey: 方法是 SDWebImage 磁盘缓存系统的核心检索逻辑,用于通过指定 key 在多个潜在路径中搜索并加载缓存的图片数据。其核心逻辑如下:

参数校验

if (!key) {return nil;
}
  • 若 key 为空(如 URL 无效或未生成缓存键),直接返回空值,避免无效操作。

默认磁盘路径检索

NSData *data = [self.diskCache dataForKey:key];
if (data) {return data;
}
  • 优先从默认缓存路径(如沙盒的 Library/Caches/default/com.hackemist.SDImageCache)加载数据;
  • 使用 dataForKey: 方法直接匹配带扩展名的文件名(如 md5_hash.jpg),符合 SDWebImage 5.0+ 的缓存命名规则。

扩展路径兼容性检索

if (self.additionalCachePathBlock) {NSString *filePath = self.additionalCachePathBlock(key);if (filePath) {data = [NSData dataWithContentsOfFile:filePath options:self.config.diskCacheReadingOptions error:nil];}
}
  • 通过回调块 additionalCachePathBlock 支持自定义搜索路径,例如:
    • 预加载的本地资源目录(如 Bundle 中的图片);
    • 旧版本遗留的缓存路径(早期版本可能未包含扩展名);

图像下载

callDownloadProcessForOperation

无论我们在缓存之中找没找到对应图影片,我们都会进入下一步callDownloadProcessForOperation之中

// 核心下载流程处理方法(SDWebImageManager内部逻辑)
// 参数说明:
// - operation: 组合操作对象,用于管理缓存和下载的取消
// - url: 目标图片URL
// - options: 下载选项(如SDWebImageRetryFailed等)
// - context: 上下文配置字典,可传递自定义参数
// - cachedImage: 已存在的缓存图片(可能为nil)
// - cachedData: 已存在的缓存二进制数据(可能为nil)
// - cacheType: 缓存类型(内存/磁盘)
// - progressBlock: 下载进度回调
// - completedBlock: 最终完成回调
- (void)callDownloadProcessForOperation:(nonnull SDWebImageCombinedOperation *)operationurl:(nonnull NSURL *)urloptions:(SDWebImageOptions)optionscontext:(SDWebImageContext *)contextcachedImage:(nullable UIImage *)cachedImagecachedData:(nullable NSData *)cachedDatacacheType:(SDImageCacheType)cacheTypeprogress:(nullable SDImageLoaderProgressBlock)progressBlockcompleted:(nullable SDInternalCompletionBlock)completedBlock {// 标记缓存操作结束(线程安全操作)@synchronized (operation) {operation.cacheOperation = nil; // 清空缓存操作引用}// 获取图片加载器(支持自定义加载器)id<SDImageLoader> imageLoader = context[SDWebImageContextImageLoader];if (!imageLoader) {imageLoader = self.imageLoader; // 使用默认加载器}// 判断是否需要下载网络图片的三重条件:// 1. 未设置仅从缓存加载选项(SDWebImageFromCacheOnly)// 2. 无缓存图片 或 设置了强制刷新选项(SDWebImageRefreshCached)// 3. 代理方法允许下载该URLBOOL shouldDownload = !SD_OPTIONS_CONTAINS(options, SDWebImageFromCacheOnly);shouldDownload &= (!cachedImage || options & SDWebImageRefreshCached);shouldDownload &= (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]);// 检查加载器是否支持该URL请求if ([imageLoader respondsToSelector:@selector(canRequestImageForURL:options:context:)]) {shouldDownload &= [imageLoader canRequestImageForURL:url options:options context:context];} else {shouldDownload &= [imageLoader canRequestImageForURL:url];}if (shouldDownload) {// 处理缓存刷新逻辑(当存在缓存但需要刷新时)if (cachedImage && options & SDWebImageRefreshCached) {// 先返回缓存图片,当启用 SDWebImageRefreshCached 选项时,即使存在缓存也会强制向服务器验证文件是否更新[self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES queue:context[SDWebImageContextCallbackQueue] url:url];// 将缓存图片传递给加载器(用于304 Not Modified判断)SDWebImageMutableContext *mutableContext = [context mutableCopy] ?: [NSMutableDictionary dictionary];mutableContext[SDWebImageContextLoaderCachedImage] = cachedImage;context = [mutableContext copy];}// 弱引用operation避免循环引用@weakify(operation);// 启动图片加载器请求operation.loaderOperation = [imageLoader requestImageWithURL:url options:options context:context progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {@strongify(operation);if (!operation || operation.isCancelled) {// 用户取消操作时的错误处理[self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"用户取消了操作"}] queue:context[SDWebImageContextCallbackQueue] url:url];} else if (cachedImage && options & SDWebImageRefreshCached && [error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorCacheNotModified) {// 服务器返回304 Not Modified时的特殊处理(不触发回调)[2](@ref)} else if ([error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorCancelled) {// 请求发送前被取消的处理[self callCompletionBlockForOperation:operation completion:completedBlock error:error queue:context[SDWebImageContextCallbackQueue] url:url];} else if (error) {// 通用错误处理[self callCompletionBlockForOperation:operation completion:completedBlock error:error queue:context[SDWebImageContextCallbackQueue] url:url];// 判断是否需要将失败URL加入黑名单BOOL shouldBlockFailedURL = [self shouldBlockFailedURLWithURL:url error:error options:options context:context];if (shouldBlockFailedURL) {SD_LOCK(self->_failedURLsLock);[self.failedURLs addObject:url]; // 加入失败URL列表SD_UNLOCK(self->_failedURLsLock);}} else {// 成功下载处理if ((options & SDWebImageRetryFailed)) {// 从失败列表中移除该URLSD_LOCK(self->_failedURLsLock);[self.failedURLs removeObject:url];SD_UNLOCK(self->_failedURLsLock);}// 进入图片转换流程(解码/缩放等处理)[self callTransformProcessForOperation:operation url:url options:options context:context originalImage:downloadedImage originalData:downloadedData cacheType:SDImageCacheTypeNone finished:finished completed:completedBlock];}if (finished) {// 清理运行中的操作[self safelyRemoveOperationFromRunning:operation];}}];} else if (cachedImage) {// 直接返回缓存图片[self callCompletionBlockForOperation:operation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES queue:context[SDWebImageContextCallbackQueue] url:url];[self safelyRemoveOperationFromRunning:operation];} else {// 无缓存且不允许下载的情况[self callCompletionBlockForOperation:operation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES queue:context[SDWebImageContextCallbackQueue] url:url];[self safelyRemoveOperationFromRunning:operation];}
}
downloadImageWithURL
// SDWebImage下载核心方法:创建并管理图片下载任务
// 参数说明:
// - url: 图片请求URL(可为空)
// - options: 下载选项(如SDWebImageDownloaderHighPriority)
// - context: 上下文字典,可传递自定义参数(如解码选项、缓存策略)
// - progressBlock: 下载进度回调
// - completedBlock: 下载完成回调
// 返回值:SDWebImageDownloadToken对象,用于取消下载任务
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)urloptions:(SDWebImageDownloaderOptions)optionscontext:(nullable SDWebImageContext *)contextprogress:(nullable SDWebImageDownloaderProgressBlock)progressBlockcompleted:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {// 空URL校验:立即触发错误回调if (url == nil) {if (completedBlock) {NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidURL userInfo:@{NSLocalizedDescriptionKey : @"图片URL为空"}];completedBlock(nil, nil, error, YES); // finished标记为YES}return nil;}id downloadOperationCancelToken;// 生成缓存键:支持自定义缓存键过滤器(用于不同尺寸缩略图场景)id<SDWebImageCacheKeyFilter> cacheKeyFilter = context[SDWebImageContextCacheKeyFilter];NSString *cacheKey = cacheKeyFilter ? [cacheKeyFilter cacheKeyForURL:url] : url.absoluteString;// 获取解码选项:结合上下文与下载选项生成最终解码配置SDImageCoderOptions *decodeOptions = SDGetDecodeOptionsFromContext(context, [self.class imageOptionsFromDownloaderOptions:options], cacheKey);// 线程安全操作:使用互斥锁保护URLOperations字典SD_LOCK(_operationsLock);// 检查现有下载操作:通过URL查找是否已有运行中的任务NSOperation<SDWebImageDownloaderOperation> *operation = self.URLOperations[url];BOOL shouldNotReuseOperation = NO;// 判断操作是否可重用:已完成的或已取消的操作需要重建[6](@ref)if (operation) {@synchronized (operation) {shouldNotReuseOperation = operation.isFinished || operation.isCancelled;}} else {shouldNotReuseOperation = YES;}// 创建新下载操作(当无可重用操作时)if (shouldNotReuseOperation) {operation = [self createDownloaderOperationWithUrl:url options:options context:context];if (!operation) {SD_UNLOCK(_operationsLock);if (completedBlock) {NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidDownloadOperation userInfo:@{NSLocalizedDescriptionKey : @"下载操作创建失败"}];completedBlock(nil, nil, error, YES);}return nil;}// 弱引用self避免循环引用__weak typeof(self) weakSelf = self;operation.completionBlock = ^{__strong typeof(weakSelf) strongSelf = weakSelf;if (!strongSelf) return;// 操作完成后从字典移除(线程安全)SD_LOCK(strongSelf->_operationsLock);[strongSelf.URLOperations removeObjectForKey:url];SD_UNLOCK(strongSelf->_operationsLock);};// 存储新操作到字典并加入队列self.URLOperations[url] = operation;downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock decodeOptions:decodeOptions];[self.downloadQueue addOperation:operation]; // 加入操作队列} else {// 重用现有操作:同步添加新的回调(线程安全)@synchronized (operation) {downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock decodeOptions:decodeOptions];}}SD_UNLOCK(_operationsLock);// 生成下载令牌:封装操作与取消令牌SDWebImageDownloadToken *token = [[SDWebImageDownloadToken alloc] initWithDownloadOperation:operation];token.url = url;token.request = operation.request;token.downloadOperationCancelToken = downloadOperationCancelToken; // 用于取消特定回调return token;
}

我们发现,在这个方法之中似乎没有实现,网络下载的执行逻辑被封装在 SDWebImageDownloaderOperation 内部,通过start方法进行下载任务

start
// SDWebImageDownloaderOperation的核心启动方法
// 功能:初始化网络请求、管理任务生命周期、处理后台任务和线程安全
- (void)start {@synchronized (self) {  // 线程安全锁,防止多线程竞争[1,5](@ref)// 检查操作是否已被取消if (self.isCancelled) {if (!self.isFinished) self.finished = YES;// 触发取消错误回调(用户主动取消场景)[self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:@{NSLocalizedDescriptionKey : @"用户取消下载操作"}]];[self reset];  // 重置内部状态return;}#if SD_UIKIT// 处理应用进入后台时的任务延续逻辑Class UIApplicationClass = NSClassFromString(@"UIApplication");BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {__weak typeof(self) wself = self;UIApplication *app = [UIApplicationClass performSelector:@selector(sharedApplication)];// 开启后台任务防止应用挂起(iOS后台下载支持)self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{[wself cancel];  // 后台时间耗尽时自动取消任务}];}
#endif// 配置NSURLSession(无可用session时创建新session)NSURLSession *session = self.unownedSession;if (!session) {NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];sessionConfig.timeoutIntervalForRequest = 15;  // 设置15秒请求超时/* 创建串行队列的session,保证代理方法顺序执行* delegateQueue设为nil时,系统自动创建串行队列*/session = [NSURLSession sessionWithConfiguration:sessionConfigdelegate:selfdelegateQueue:nil];self.ownedSession = session;  // 持有session所有权[6](@ref)}// 处理忽略缓存响应选项(SDWebImageDownloaderIgnoreCachedResponse)if (self.options & SDWebImageDownloaderIgnoreCachedResponse) {NSURLCache *URLCache = session.configuration.URLCache ?: [NSURLCache sharedURLCache];NSCachedURLResponse *cachedResponse;@synchronized (URLCache) {  // NSURLCache线程安全访问[2](@ref)cachedResponse = [URLCache cachedResponseForRequest:self.request];}if (cachedResponse) {self.cachedData = cachedResponse.data;     // 存储缓存数据self.response = cachedResponse.response;   // 存储缓存响应头}}// 异常检查:session代理不可用if (!session.delegate) {[self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidDownloadOperation userInfo:@{NSLocalizedDescriptionKey : @"Session代理失效"}]];[self reset];return;}// 创建数据任务并启动self.dataTask = [session dataTaskWithRequest:self.request];self.executing = YES;  // 标记操作进入执行状态[5](@ref)}  // 结束@synchronized块// 配置任务优先级并启动if (self.dataTask) {// 设置任务优先级(高/低/默认)if (self.options & SDWebImageDownloaderHighPriority) {self.dataTask.priority = NSURLSessionTaskPriorityHigh;} else if (self.options & SDWebImageDownloaderLowPriority) {self.dataTask.priority = NSURLSessionTaskPriorityLow;} else {self.dataTask.priority = NSURLSessionTaskPriorityDefault;}[self.dataTask resume];  // 启动网络请求[1](@ref)// 触发初始进度回调(0%进度)NSArray<SDWebImageDownloaderOperationToken *> *tokens;@synchronized (self) {tokens = [self.callbackTokens copy];}for (SDWebImageDownloaderOperationToken *token in tokens) {if (token.progressBlock) {token.progressBlock(0, NSURLResponseUnknownLength, self.request.URL);}}// 发送下载开始通知(主线程异步)__block typeof(self) strongSelf = self;dispatch_async(dispatch_get_main_queue(), ^{[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:strongSelf];});} else {// 任务创建失败处理if (!self.isFinished) self.finished = YES;[self callCompletionBlocksWithError:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidDownloadOperation userInfo:@{NSLocalizedDescriptionKey : @"任务初始化失败"}]];[self reset];}
}

总结流程

官方SDWebImage的流程图

http://www.xdnf.cn/news/593587.html

相关文章:

  • 容器资源绑定和查看
  • 中医方剂 - 理中汤
  • 车载网关策略 --- 车载网关重置前的请求转发机制
  • HarmonyOS学习——UIAbility组件(上)
  • 有监督学习——决策树
  • 咬合配准算法文献推荐
  • 机器学习圣经PRML作者Bishop20年后新作中文版出版!
  • Apollo10.0学习——planning模块(10)之依赖注入器injector_
  • 交换机工作原理解析与网络安全实践
  • 4个关键功能,让健康管理系统真正发挥作用
  • 基于Java的体育场馆预约系统的设计与实现【附源码】
  • Web3.0:下一代互联网的变革与机遇
  • [原创](现代Delphi 12指南):[macOS 64bit App开发]: 如何获取目标App的程序图标?
  • 论文解读 | 《桑黄提取物对小鼠宫颈癌皮下移植瘤的抑制及机制研究》
  • 深入理解线程池:参数、流程与实战应用
  • 【C++进阶篇】红黑树的实现(赋源码)
  • SIL2/PLd 认证 Inxpect毫米波安全雷达:3D 扫描 + 微小运动检测守护工业安全
  • 多旋翼无人机架空输电线路自动化巡检方案
  • 从3.7V/5V到7.4V,FP6291在应急供电智能门锁中的应用
  • NV039NV044美光闪存颗粒NV047NV053
  • 论文解读 |《药用真菌桑黄化学成分的研究》
  • String.join()-高效字符串拼接
  • 重排序模型计算两个文本的分数
  • CentOS7挂载hgfs文件夹(VMware 共享文件夹)及网卡的自启动。
  • framework 编译技巧
  • 探索微分方程的领域及AI推理
  • 页面置换算法概述
  • WebView2 Win7下部分机器触屏失效的问题
  • electron 控制台打印中文乱码问题
  • Selenium自动化测试入门:cookie 处理