美文网首页
SDWebImage加载、缓存的处理

SDWebImage加载、缓存的处理

作者: 沉江小鱼 | 来源:发表于2019-09-26 09:59 被阅读0次

    SDWebImage 的github上有它的UML图:


    SDWebImageClassDiagram.png

    5.0版本之后做了一些类的重构,把里面小的组件抽离出来作为一个独立的库。

    我们应该学会看懂它,这里简单的记录一下:

    泛化(generalization):

    表示is-a的关系,是对象之间耦合度最大的一种关系,子类继承父类的所有细节。在类图中使用带有三角箭头的实线表示,箭头从子类指向父类。
    下面这张图我们可以看到 SDMemoryCache 就是继承的系统的 NSCache类:


    屏幕快照 2019-09-03 下午9.45.20.png

    实现(realization):

    在类图中就是接口和实现的关系,在类图中使用带三角箭头的虚线表示,箭头从实现类指向接口。
    5.0之后,SDWebImage也是主要采用了协议解耦。
    下面这张图就是SDWebImage中实现接口的例子:


    屏幕快照 2019-09-03 下午9.47.19.png

    聚合(aggregation)

    表示has-a的关系,是一种不稳定的包含关系。较强于一般关联,有整体和局部的关系,并且没有了整体,局部也可单独存在。如公司和员工的关系,公司包含员工,但公司倒闭,员工依然可以换公司。在类图中使用空心的菱形表示,菱形从局部指向整体。
    下图就是SDWebImage中的聚合关系:


    屏幕快照 2019-09-03 下午9.50.32.png

    组合(composition)

    表示contains-a的关系,是一种强烈的包含关系。组合类负责被组合类的生命周期。是一种更强的聚合关系。部分不能脱离整体存在。比如公司和部门的关系,没有了公司,部门也就不存在了,调查问卷中问题和选项的关系,订单和订单选项的关系。在类图使用实心的菱形表示,菱形从局部指向整体。
    下面是 SDWebImage 4.0的组合关系的局部图:


    屏幕快照 2019-09-03 下午9.53.00.png

    我们可以抽时间对SDWebImage有一个整体的认识,项目中我们用的最多的应该就是UIImageView+WebCache.m的这个方法了:

    - (void)sd_setImageWithURL:(nullable NSURL *)url
              placeholderImage:(nullable UIImage *)placeholder;
    

    我们可以看到,这个方法最终会调用到下面这个方法(还是在当前文件):

    - (void)sd_setImageWithURL:(nullable NSURL *)url
              placeholderImage:(nullable UIImage *)placeholder
                       options:(SDWebImageOptions)options
                       context:(nullable SDWebImageContext *)context
                      progress:(nullable SDImageLoaderProgressBlock)progressBlock
                     completed:(nullable SDExternalCompletionBlock)completedBlock {
        [self sd_internalSetImageWithURL:url
                        placeholderImage:placeholder
                                 options:options
                                 context:context
                           setImageBlock:nil
                                progress:progressBlock
                               completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, SDImageCacheType cacheType, BOOL finished, NSURL * _Nullable imageURL) {
                                   if (completedBlock) {
                                       completedBlock(image, error, cacheType, imageURL);
                                   }
                               }];
    }
    

    这个方法最主要的事情就是设置图片,设置图片的事情是交由Manager去管理的,现在我们继续看这个方法:

    - (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                      placeholderImage:(nullable UIImage *)placeholder
                               options:(SDWebImageOptions)options
                               context:(nullable SDWebImageContext *)context
                         setImageBlock:(nullable SDSetImageBlock)setImageBlock
                              progress:(nullable SDImageLoaderProgressBlock)progressBlock
                             completed:(nullable SDInternalCompletionBlock)completedBlock {
        context = [context copy]; // copy to avoid mutable object
        
        // 判断当前控件上是否有其他任务,有的话取消,比如Cell复用时,以新的为主
        NSString *validOperationKey = context[SDWebImageContextSetImageOperationKey];
        if (!validOperationKey) {
            validOperationKey = NSStringFromClass([self class]);
        }
        self.sd_latestOperationKey = validOperationKey;
        [self sd_cancelImageLoadOperationWithKey:validOperationKey];
        self.sd_imageURL = url;
        
    // 这段代码的主要作用就是判断如果当前的options不是延迟设置placeholder,就回到主线程当中设置placeholder。
        if (!(options & SDWebImageDelayPlaceholder)) {
            dispatch_main_async_safe(^{
                [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:SDImageCacheTypeNone imageURL:url];
            });
        }
        
        if (url) {
            // 重置下载进度
            NSProgress *imageProgress = objc_getAssociatedObject(self, @selector(sd_imageProgress));
            if (imageProgress) {
                imageProgress.totalUnitCount = 0;
                imageProgress.completedUnitCount = 0;
            }
            
    #if SD_UIKIT || SD_MAC
            // check and start image indicator
            [self sd_startImageIndicator];
            id<SDWebImageIndicator> imageIndicator = self.sd_imageIndicator;
    #endif
            
            SDWebImageManager *manager = context[SDWebImageContextCustomManager];
            if (!manager) {
                manager = [SDWebImageManager sharedManager];
            }
            
    // 这个就是设置下载进度回调,修改下载进度,显示imageIndicator
            SDImageLoaderProgressBlock combinedProgressBlock = ^(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL) {
                if (imageProgress) {
                    imageProgress.totalUnitCount = expectedSize;
                    imageProgress.completedUnitCount = receivedSize;
                }
    #if SD_UIKIT || SD_MAC
                if ([imageIndicator respondsToSelector:@selector(updateIndicatorProgress:)]) {
                    double progress = 0;
                    if (expectedSize != 0) {
                        progress = (double)receivedSize / expectedSize;
                    }
                    progress = MAX(MIN(progress, 1), 0); // 0.0 - 1.0
                    dispatch_async(dispatch_get_main_queue(), ^{
                        [imageIndicator updateIndicatorProgress:progress];
                    });
                }
    #endif
                if (progressBlock) {
                    progressBlock(receivedSize, expectedSize, targetURL);
                }
            };
            @weakify(self);
          // 最重要的方法
    // 加载图片
            id <SDWebImageOperation> 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 (imageProgress && finished && !error && imageProgress.totalUnitCount == 0 && imageProgress.completedUnitCount == 0) {
                    imageProgress.totalUnitCount = SDWebImageProgressUnitCountUnknown;
                    imageProgress.completedUnitCount = SDWebImageProgressUnitCountUnknown;
                }
                
    #if SD_UIKIT || SD_MAC
                // 停止 image indicator
                if (finished) {
                    [self sd_stopImageIndicator];
                }
    #endif
                // 这个就是先通过options判断,是否需要获取到图片之后,自动显示,如果不需要设置image,这里会调用完 completedBlock 直接就return了。
                BOOL shouldCallCompletedBlock = finished || (options & SDWebImageAvoidAutoSetImage);
                BOOL shouldNotSetImage = ((image && (options & SDWebImageAvoidAutoSetImage)) ||
                                          (!image && !(options & SDWebImageDelayPlaceholder)));
                SDWebImageNoParamsBlock callCompletedBlockClojure = ^{
                    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 set
                if (shouldNotSetImage) {
        // 如果不需要设置image,那么这里直接走回调的block,然后return了。               dispatch_main_async_safe(callCompletedBlockClojure);
                    return;
                }
                
    // 如果允许设置图片,就开始设置显示图片了
                UIImage *targetImage = nil;
                NSData *targetData = nil;
                if (image) {
                    // case 2a: 如果options 不是SDWebImageAvoidAutoSetImage
                    targetImage = image;
                    targetData = data;
                } else if (options & SDWebImageDelayPlaceholder) {
                    // case 2b: we got no image and the SDWebImageDelayPlaceholder flag is set
                    targetImage = placeholder;
                    targetData = nil;
                }
                
    #if SD_UIKIT || SD_MAC
                // check whether we should use the image transition
                SDWebImageTransition *transition = nil;
                if (finished && (options & SDWebImageForceTransition || cacheType == SDImageCacheTypeNone)) {
                    transition = self.sd_imageTransition;
                }
    #endif
                dispatch_main_async_safe(^{
    #if SD_UIKIT || SD_MAC
                    [self sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock transition:transition cacheType:cacheType imageURL:imageURL];
    #else
                    [self sd_setImage:targetImage imageData:targetData basedOnClassOrViaCustomSetImageBlock:setImageBlock cacheType:cacheType imageURL:imageURL];
    #endif
                    callCompletedBlockClojure();
                });
            }];
            [self sd_setImageLoadOperation:operation forKey:validOperationKey];
        } else {
    #if SD_UIKIT || SD_MAC
            [self sd_stopImageIndicator];
    #endif
    // 完成的回调
            dispatch_main_async_safe(^{
                if (completedBlock) {
                    NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidURL userInfo:@{NSLocalizedDescriptionKey : @"Image url is nil"}];
                    completedBlock(nil, nil, error, SDImageCacheTypeNone, YES, url);
                }
            });
        }
    }
    

    我们现在可以一点一点分析一下:

    //判断当前控件上是否有其他任务,有的话取消,比如Cell复用时,以新的为主
        NSString *validOperationKey = context[SDWebImageContextSetImageOperationKey];
        if (!validOperationKey) {
            validOperationKey = NSStringFromClass([self class]);
        }
        self.sd_latestOperationKey = validOperationKey;
        [self sd_cancelImageLoadOperationWithKey:validOperationKey];
    ========> sd_cancelImageLoadOperationWithKey方法:
    // 这个方法就是调用了operation的cancel方法
    - (void)sd_cancelImageLoadOperationWithKey:(nullable NSString *)key {
        if (key) {
            // Cancel in progress downloader from queue
            SDOperationsDictionary *operationDictionary = [self sd_operationDictionary];
            id<SDWebImageOperation> operation;
            
            @synchronized (self) {
                operation = [operationDictionary objectForKey:key];
            }
            if (operation) {
                if ([operation conformsToProtocol:@protocol(SDWebImageOperation)]) {
                    [operation cancel];
                }
                @synchronized (self) {
                    [operationDictionary removeObjectForKey:key];
                }
            }
        }
    }
    

    这个operation是怎么来的,又是怎么添加进 SDOperationsDictionary 的,我们可以继续向下看,去掉无关的代码,我们可以看到:

    - (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                      placeholderImage:(nullable UIImage *)placeholder
                               options:(SDWebImageOptions)options
                               context:(nullable SDWebImageContext *)context
                         setImageBlock:(nullable SDSetImageBlock)setImageBlock
                              progress:(nullable SDImageLoaderProgressBlock)progressBlock
                             completed:(nullable SDInternalCompletionBlock)completedBlock {    
    ...
     // 1.第一步判断是否有旧的任务,有的话就取消
        NSString *validOperationKey = context[SDWebImageContextSetImageOperationKey];
        if (!validOperationKey) {
            validOperationKey = NSStringFromClass([self class]);
        }
        self.sd_latestOperationKey = validOperationKey;
        [self  sd_cancelImageLoadOperationWithKey:validOperationKey];
        self.sd_imageURL = url;
        
    ...
    // 2.第二步构造一个新的operation
        id <SDWebImageOperation> operation = ...
    
    // 3.第三部添加进去
        [self sd_setImageLoadOperation:operation forKey:validOperationKey];    
    ...
    }
    

    这就是一整个取消和添加的流程,这个operation实现了 SDWebImageOperation,这个协议定义最主要的行为就是cancel操作:

    /// A protocol represents cancelable operation.
    @protocol SDWebImageOperation <NSObject>
    
    - (void)cancel;
    
    @end
    

    现在又要到关键的一步了,也就是构建operation的方法,要真正的去开始一个任务了:

    - (SDWebImageCombinedOperation *)loadImageWithURL:(nullable NSURL *)url
                                              options:(SDWebImageOptions)options
                                              context:(nullable SDWebImageContext *)context
                                             progress:(nullable SDImageLoaderProgressBlock)progressBlock
                                            completed:(nonnull SDInternalCompletionBlock)completedBlock {
        // Invoking this method without a completedBlock is pointless
        NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -[SDWebImagePrefetcher prefetchURLs] instead");
    
        // 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 isKindOfClass:NSString.class]) {
            url = [NSURL URLWithString:(NSString *)url];
        }
    
        // Prevents app crashing on argument type error like sending NSNull instead of NSURL
        if (![url isKindOfClass:NSURL.class]) {
            url = nil;
        }
    
    // 用来耦合缓存任务和下载任务
        SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
        operation.manager = self;
    
        BOOL isFailedUrl = NO;
        if (url) {
            SD_LOCK(self.failedURLsLock);
            isFailedUrl = [self.failedURLs containsObject:url];
            SD_UNLOCK(self.failedURLsLock);
        }
    
        if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
            [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorInvalidURL userInfo:@{NSLocalizedDescriptionKey : @"Image url is nil"}] url:url];
            return operation;
        }
    
        SD_LOCK(self.runningOperationsLock);
        [self.runningOperations addObject:operation];
        SD_UNLOCK(self.runningOperationsLock);
        
        // Preprocess the options and context arg to decide the final the result for manager
        SDWebImageOptionsResult *result = [self processedResultForURL:url options:options context:context];
        
        // 开始从缓存中加载图片,优先加载缓存,它会创建一个缓存查找任务,然后给我们当前创建的 SDWebImageCombinedOperation 赋值
        [self callCacheProcessForOperation:operation url:url options:result.options context:result.context progress:progressBlock completed:completedBlock];
    
        return operation;
    }
    
    

    上面的方法的一个补充:

    // 用来耦合缓存任务和下载任务
        SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
        operation.manager = self;
    ======> SDWebImageCombinedOperation的具体实现:
    // SDWebImageCombinedOperation 也实现了 < SDWebImageOperation >协议
    @interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>
    // 取消
    - (void)cancel;
    // 缓存任务
    @property (strong, nonatomic, nullable, readonly) id<SDWebImageOperation> cacheOperation;
    // 下载任务
    @property (strong, nonatomic, nullable, readonly) id<SDWebImageOperation> loaderOperation;
    @end
    // 当取消任务的时候,实际上也是同时取消了缓存任务和下载任务:
    ======> SDWebImageCombinedOperation  cancel方法:
    - (void)cancel {
        @synchronized(self) {
            if (self.isCancelled) {
                return;
            }
            self.cancelled = YES;
            if (self.cacheOperation) {
                [self.cacheOperation cancel];
                self.cacheOperation = nil;
            }
            if (self.loaderOperation) {
                [self.loaderOperation cancel];
                self.loaderOperation = nil;
            }
            [self.manager safelyRemoveOperationFromRunning:self];
        }
    }
    

    我们现在要去看如何创建缓存查找任务:

    // Query cache process
    - (void)callCacheProcessForOperation:(nonnull SDWebImageCombinedOperation *)operation
                                     url:(nonnull NSURL *)url
                                 options:(SDWebImageOptions)options
                                 context:(nullable SDWebImageContext *)context
                                progress:(nullable SDImageLoaderProgressBlock)progressBlock
                               completed:(nullable SDInternalCompletionBlock)completedBlock {
        // Check whether we should query cache
        BOOL shouldQueryCache = !SD_OPTIONS_CONTAINS(options, SDWebImageFromLoaderOnly);
        if (shouldQueryCache) {
            id<SDWebImageCacheKeyFilter> cacheKeyFilter = context[SDWebImageContextCacheKeyFilter];
            NSString *key = [self cacheKeyForURL:url cacheKeyFilter:cacheKeyFilter];
            @weakify(operation);
    // 创建一个缓存查找任务,赋值给上一个方法中的SDWebImageCombinedOperation 的实例
            operation.cacheOperation = [self.imageCache queryImageForKey:key options:options context:context completion:^(UIImage * _Nullable cachedImage, NSData * _Nullable cachedData, SDImageCacheType cacheType) {
                @strongify(operation);
                if (!operation || operation.isCancelled) {
                    // Image combined operation cancelled by user
                    [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:SDWebImageErrorDomain code:SDWebImageErrorCancelled userInfo:nil] url:url];
                    [self safelyRemoveOperationFromRunning:operation];
                    return;
                }
                // Continue download process
                [self callDownloadProcessForOperation:operation url:url options:options context:context cachedImage:cachedImage cachedData:cachedData cacheType:cacheType progress:progressBlock completed:completedBlock];
            }];
        } else {
            // Continue download process
            [self callDownloadProcessForOperation:operation url:url options:options context:context cachedImage:nil cachedData:nil cacheType:SDImageCacheTypeNone progress:progressBlock completed:completedBlock];
        }
    }
    
    

    我们在上面这个方法中可以看到,使用了 self.imageCache 构建了一个缓存operation,那么这个缓存是如何实现的呢?

    - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options context:(nullable SDWebImageContext *)context done:(nullable SDImageCacheQueryCompletionBlock)doneBlock {
     
        // 1.首先查找内存缓存中当前的image
        UIImage *image = [self imageFromMemoryCacheForKey:key];
        
        if (image) {
            if (options & SDImageCacheDecodeFirstFrameOnly) {
                // Ensure static image
                Class animatedImageClass = image.class;
                if (image.sd_isAnimated || ([animatedImageClass isSubclassOfClass:[UIImage class]] && [animatedImageClass conformsToProtocol:@protocol(SDAnimatedImage)])) {
    #if SD_MAC
                    image = [[NSImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:kCGImagePropertyOrientationUp];
    #else
                    image = [[UIImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:image.imageOrientation];
    #endif
                }
            } else if (options & SDImageCacheMatchAnimatedImageClass) {
                // Check image class matching
                Class animatedImageClass = image.class;
                Class desiredImageClass = context[SDWebImageContextAnimatedImageClass];
                if (desiredImageClass && ![animatedImageClass isSubclassOfClass:desiredImageClass]) {
                    image = nil;
                }
            }
        }
    
        BOOL shouldQueryMemoryOnly = (image && !(options & SDImageCacheQueryMemoryData));
        if (shouldQueryMemoryOnly) {
            if (doneBlock) {
                doneBlock(image, nil, SDImageCacheTypeMemory);
            }
            return nil;
        }
        
        // Second check the disk cache...
        NSOperation *operation = [NSOperation new];
        // Check whether we need to synchronously query disk
        // 1. in-memory cache hit & memoryDataSync
        // 2. in-memory cache miss & diskDataSync
        BOOL shouldQueryDiskSync = ((image && options & SDImageCacheQueryMemoryDataSync) ||
                                    (!image && options & SDImageCacheQueryDiskDataSync));
        void(^queryDiskBlock)(void) =  ^{
            if (operation.isCancelled) {
                if (doneBlock) {
                    doneBlock(nil, nil, SDImageCacheTypeNone);
                }
                return;
            }
            
            @autoreleasepool {
                NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
                UIImage *diskImage;
                SDImageCacheType cacheType = SDImageCacheTypeNone;
                if (image) {
                    // the image is from in-memory cache, but need image data
                    diskImage = image;
                    cacheType = SDImageCacheTypeMemory;
                } else if (diskData) {
                    cacheType = SDImageCacheTypeDisk;
                    // decode image data only if in-memory cache missed
                    diskImage = [self diskImageForKey:key data:diskData options:options context:context];
                    if (diskImage && self.config.shouldCacheImagesInMemory) {
                        NSUInteger cost = diskImage.sd_memoryCost;
                        [self.memoryCache setObject:diskImage forKey:key cost:cost];
                    }
                }
                
                if (doneBlock) {
                    if (shouldQueryDiskSync) {
                        doneBlock(diskImage, diskData, cacheType);
                    } else {
                        dispatch_async(dispatch_get_main_queue(), ^{
                            doneBlock(diskImage, diskData, cacheType);
                        });
                    }
                }
            }
        };
        
        // Query in ioQueue to keep IO-safe
        if (shouldQueryDiskSync) {
            dispatch_sync(self.ioQueue, queryDiskBlock);
        } else {
            dispatch_async(self.ioQueue, queryDiskBlock);
        }
        
        return operation;
    }
    
    

    我们删除无用的代码,可以看到第一步是先从内存中查找图片:

    // 首先查找内存缓存中当前的image
        UIImage *image = [self imageFromMemoryCacheForKey:key];
    =====> imageFromMemoryCacheForKey 方法实现:
    - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key {
        return [self.memoryCache objectForKey:key];
    }
    我们可以看到 memoryCache 其实就是:
    @property (nonatomic, strong, readonly, nonnull) id<SDMemoryCache> memoryCache;
    它实际上就是一个遵循了SDMemoryCache 协议的 SDMemoryCache对象(在代码中可以看到):
    ======> SDMemoryCache 协议:
    这个协议主要是定义了一个缓存必须的存入查找的方法列表
    @protocol SDMemoryCache <NSObject>
    - (nonnull instancetype)initWithConfig:(nonnull SDImageCacheConfig *)config;
    - (nullable id)objectForKey:(nonnull id)key;
    - (void)setObject:(nullable id)object forKey:(nonnull id)key;
    - (void)setObject:(nullable id)object forKey:(nonnull id)key cost:(NSUInteger)cost;
    - (void)removeObjectForKey:(nonnull id)key;
    - (void)removeAllObjects;
    @end
    ======> SDMemoryCache类:继承自系统的NSCache
    @interface SDMemoryCache <KeyType, ObjectType> : NSCache <KeyType, ObjectType> <SDMemoryCache>
    @property (nonatomic, strong, nonnull, readonly) SDImageCacheConfig *config;
    @end
    

    这里定义一个 SDMemoryCache协议主要是为了解耦,不想因为继承NSCache造成的耦合。
    SDWebImage 5.0之后,主要是使用协议进行解耦。

    NSCache另写了一篇,来进行补充。

    接下来看下SDWebImage是如何处理NSURLCache的,可以先看下NSURLCache的相关文章。

    SDWebImage避免NSURLCache的相关操作:
    SDWebImageDownloader.m 文件中

    // 如果用户不设置使用NSURLCache,就默认忽略本地缓存,防止重复缓存
        //
        NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
        NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:cachePolicy timeoutInterval:timeoutInterval];
        mutableRequest.HTTPShouldHandleCookies = SD_OPTIONS_CONTAINS(options, SDWebImageDownloaderHandleCookies);
        mutableRequest.HTTPShouldUsePipelining = YES;
        SD_LOCK(self.HTTPHeadersLock);
        mutableRequest.allHTTPHeaderFields = self.HTTPHeaders;
        SD_UNLOCK(self.HTTPHeadersLock);
        id<SDWebImageDownloaderRequestModifier> requestModifier;
        if ([context valueForKey:SDWebImageContextDownloadRequestModifier]) {
            requestModifier = [context valueForKey:SDWebImageContextDownloadRequestModifier];
        } else {
            requestModifier = self.requestModifier;
        }
    

    SDWebImageDownloaderOperation.m 文件中:

    - (void)URLSession:(NSURLSession *)session
              dataTask:(NSURLSessionDataTask *)dataTask
     willCacheResponse:(NSCachedURLResponse *)proposedResponse
     completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
        
        NSCachedURLResponse *cachedResponse = proposedResponse;
    
        // 这个就是避免本地缓存
        if (!(self.options & SDWebImageDownloaderUseNSURLCache)) {
            // Prevents caching of responses
            cachedResponse = nil;
        }
        if (completionHandler) {
            completionHandler(cachedResponse);
        }
    }
    

    如果缓存策略为SDWebImageDownloaderIgnoreCachedResponse忽略本地缓存,SDWebImage会对比本地缓存和服务器数据是否一样,一样的话,则会返回304的错误,这里并不是真正的错误,只是为了方便之后的处理。


    屏幕快照 2019-09-22 下午6.50.26.png

    相关文章

      网友评论

          本文标题:SDWebImage加载、缓存的处理

          本文链接:https://www.haomeiwen.com/subject/fkrkyctx.html