前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[iOS源码笔记]·第三方网络图片处理框架:SDWebImage网络下载及缓存管理策略

[iOS源码笔记]·第三方网络图片处理框架:SDWebImage网络下载及缓存管理策略

作者头像
陈满iOS
发布2018-09-10 11:11:14
2K0
发布2018-09-10 11:11:14
举报
文章被收录于专栏:陈满iOS陈满iOS

目前,虽然AFNetworking也提供了图片下载及缓存策略,但更专业的时候,图片加载框架一般用SDWebImage,正如主流APP(如QQ和微信)会用到。这里介绍一个查询主流APP主要框架的网站:AppSight

官方Demo演示效果

1.框架简介

1.1 使用入门
代码语言:javascript
复制
Objective-C:

#import <SDWebImage/UIImageView+WebCache.h>
...
[imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
             placeholderImage:[UIImage imageNamed:@"placeholder.png"]];
代码语言:javascript
复制
Swift:

import SDWebImage

imageView.sd_setImage(with: URL(string: "http://www.domain.com/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
1.2 官方文档

2.官方Demo调用实例解析

  • 调用主体:

MasterViewController.m

  • 导入文件:
代码语言:javascript
复制
#import <SDWebImage/UIImageView+WebCache.h>
  • 调用方法:

...}

代码语言:javascript
复制
- 调用实例:
  • (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; static UIImage *placeholderImage = nil; if (!placeholderImage) { placeholderImage = UIImage imageNamed:@"placeholder"; } MyCustomTableViewCell *cell = tableView dequeueReusableCellWithIdentifier:CellIdentifier; if (cell == nil) { cell = [MyCustomTableViewCell alloc initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } cell.customImageView sd_setShowActivityIndicatorView:YES; cell.customImageView sd_setIndicatorStyle:UIActivityIndicatorViewStyleGray; cell.customTextLabel.text = NSString stringWithFormat:@"Image #%ld", (long)indexPath.row; [cell.customImageView sd_setImageWithURL:[NSURL URLWithString:_objectsindexPath.row] placeholderImage:placeholderImage options:indexPath.row == 0 ? SDWebImageRefreshCached : 0]; return cell; }
代码语言:javascript
复制
## 3.SDWebImage框架调用栈

- 框架体系

![框架体系](http://upload-images.jianshu.io/upload_images/1283539-048d262fac5403a7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

- 方法调用栈

![方法调用栈](http://upload-images.jianshu.io/upload_images/1283539-576675a2a13bf4c9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


#### 3.1 第一层
- 类 

> **FLAnimatedImageView+WebCache.m**

- 方法

> ```- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
...}
  • 实现
代码语言:javascript
复制
- (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options {
    [self sd_setImageWithURL:url placeholderImage:placeholder options:options progress:nil completed:nil];
}
3.2 第二层

FLAnimatedImageView+WebCache.m

  • 方法:

代码语言:javascript
复制
      placeholderImage:(nullable UIImage *)placeholder
               options:(SDWebImageOptions)options
              progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
             completed:(nullable SDExternalCompletionBlock)completedBlock {

...}

代码语言:javascript
复制
- 作用:

> - 根据回调图设置自己的image属性
- 调用下一栈层,获取回调图

- 实现:
  • (void)sd_setImageWithURL:(nullable NSURL *)url placeholderImage:(nullable UIImage *)placeholder options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDExternalCompletionBlock)completedBlock { __weak typeof(self)weakSelf = self; [self sd_internalSetImageWithURL:url placeholderImage:placeholder options:options operationKey:nil setImageBlock:^(UIImage *image, NSData *imageData) { SDImageFormat imageFormat = NSData sd_imageFormatForImageData:imageData; if (imageFormat == SDImageFormatGIF) { weakSelf.animatedImage = FLAnimatedImage animatedImageWithGIFData:imageData; weakSelf.image = nil; } else { weakSelf.image = image; weakSelf.animatedImage = nil; } } progress:progressBlock completed:completedBlock];

}

代码语言:javascript
复制
其中:

typedef void(^SDExternalCompletionBlock)(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL);

代码语言:javascript
复制
#### 3.3 第三层 

- 类:

> **UIView+WebCache.m**

- 方法:

> ```- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholder
                           options:(SDWebImageOptions)options
                      operationKey:(nullable NSString *)operationKey
                     setImageBlock:(nullable SDSetImageBlock)setImageBlock
                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                         completed:(nullable SDExternalCompletionBlock)completedBlock {
...}
  • 作用:

  • 判断条件用占位图返回给上一栈层
  • 调用下一栈层,根据Url加载图片
  • 实现:
代码语言:javascript
复制
- (void)sd_internalSetImageWithURL:(nullable NSURL *)url
                  placeholderImage:(nullable UIImage *)placeholder
                           options:(SDWebImageOptions)options
                      operationKey:(nullable NSString *)operationKey
                     setImageBlock:(nullable SDSetImageBlock)setImageBlock
                          progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                         completed:(nullable SDExternalCompletionBlock)completedBlock {
    
    NSString *validOperationKey = operationKey ?: NSStringFromClass([self class]);
    [self sd_cancelImageLoadOperationWithKey:validOperationKey];
    objc_setAssociatedObject(self, &imageURLKey, url, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    if (!(options & SDWebImageDelayPlaceholder)) {
        dispatch_main_async_safe(^{
            [self sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
        });
    }
    
    if (url) {
        // check if activityView is enabled or not
        if ([self sd_showActivityIndicatorView]) {
            [self sd_addActivityIndicator];
        }
        
        __weak __typeof(self)wself = self;
        id <SDWebImageOperation> operation = [SDWebImageManager.sharedManager loadImageWithURL:url options:options progress:progressBlock completed:^(UIImage *image, NSData *data, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
            __strong __typeof (wself) sself = wself;
            [sself sd_removeActivityIndicator];
            if (!sself) {
                return;
            }
            dispatch_main_async_safe(^{
                if (!sself) {
                    return;
                }
                if (image && (options & SDWebImageAvoidAutoSetImage) && completedBlock) {
                    completedBlock(image, error, cacheType, url);
                    return;
                } else if (image) {
                    [sself sd_setImage:image imageData:data basedOnClassOrViaCustomSetImageBlock:setImageBlock];
                    [sself sd_setNeedsLayout];
                } else {
                    if ((options & SDWebImageDelayPlaceholder)) {
                        [sself sd_setImage:placeholder imageData:nil basedOnClassOrViaCustomSetImageBlock:setImageBlock];
                        [sself sd_setNeedsLayout];
                    }
                }
                if (completedBlock && finished) {
                    completedBlock(image, error, cacheType, url);
                }
            });
        }];
        [self sd_setImageLoadOperation:operation forKey:validOperationKey];
    } else {
        dispatch_main_async_safe(^{
            [self sd_removeActivityIndicator];
            if (completedBlock) {
                NSError *error = [NSError errorWithDomain:SDWebImageErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey : @"Trying to load a nil url"}];
                completedBlock(nil, error, SDImageCacheTypeNone, url);
            }
        });
    }
}

3.4 第四层

  • 类名

SDWebImageManager.m

  • 方法

代码语言:javascript
复制
                                 options:(SDWebImageOptions)options
                                progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                               completed:(nullable SDInternalCompletionBlock)completedBlock {

...}

代码语言:javascript
复制
- 作用

 - 缓存策略利用

 > 执行完缓存策略后,回调获取缓存图片数据:
如满足条件(`cachedImage && options & SDWebImageRefreshCached`),则向上一栈层返回缓存图片。
```[self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
代码语言:txt
复制
- 下载策略利用

>  执行完下载策略后,回调下载图片:
>  如有需要(`SDWebImageTransformAnimatedImage`),转换格式后,保存到缓存
>  

```javascript

self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil;

代码语言:txt
复制
如无必要,则直接保存到缓存

```javascript

self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil;

代码语言:txt
复制
- 实现

```javascript
  • (id <SDWebImageOperation>)loadImageWithURL:(nullable NSURL *)url options:(SDWebImageOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDInternalCompletionBlock)completedBlock { // Invoking this method without a completedBlock is pointless NSAssert(completedBlock != nil, @"If you mean to prefetch the image, use -SDWebImagePrefetcher prefetchURLs instead");
代码语言:txt
复制
// Very common mistake is to send the URL using NSString object instead of NSURL. For some strange reason, Xcode won't
代码语言:txt
复制
// throw any warning for this type mismatch. Here we failsafe this error by allowing URLs to be passed as NSString.
代码语言:txt
复制
if ([url isKindOfClass:NSString.class]) {
代码语言:txt
复制
    url = [NSURL URLWithString:(NSString *)url];
代码语言:txt
复制
}
代码语言:txt
复制
// Prevents app crashing on argument type error like sending NSNull instead of NSURL
代码语言:txt
复制
if (![url isKindOfClass:NSURL.class]) {
代码语言:txt
复制
    url = nil;
代码语言:txt
复制
}
代码语言:txt
复制
__block SDWebImageCombinedOperation *operation = [SDWebImageCombinedOperation new];
代码语言:txt
复制
__weak SDWebImageCombinedOperation *weakOperation = operation;
代码语言:txt
复制
BOOL isFailedUrl = NO;
代码语言:txt
复制
if (url) {
代码语言:txt
复制
    @synchronized (self.failedURLs) {
代码语言:txt
复制
        isFailedUrl = [self.failedURLs containsObject:url];
代码语言:txt
复制
    }
代码语言:txt
复制
}
代码语言:txt
复制
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) {
代码语言:txt
复制
    [self callCompletionBlockForOperation:operation completion:completedBlock error:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil] url:url];
代码语言:txt
复制
    return operation;
代码语言:txt
复制
}
代码语言:txt
复制
@synchronized (self.runningOperations) {
代码语言:txt
复制
    [self.runningOperations addObject:operation];
代码语言:txt
复制
}
代码语言:txt
复制
NSString *key = [self cacheKeyForURL:url];
代码语言:txt
复制
//缓存策略
代码语言:txt
复制
operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
代码语言:txt
复制
    if (operation.isCancelled) {
代码语言:txt
复制
        [self safelyRemoveOperationFromRunning:operation];
代码语言:txt
复制
        return;
代码语言:txt
复制
    }
代码语言:txt
复制
    if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
代码语言:txt
复制
        if (cachedImage && options & SDWebImageRefreshCached) {
代码语言:txt
复制
            // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
代码语言:txt
复制
            // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.
代码语言:txt
复制
            [self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
代码语言:txt
复制
        }
代码语言:txt
复制
        // download if no image or requested to refresh anyway, and download allowed by delegate
代码语言:txt
复制
        SDWebImageDownloaderOptions downloaderOptions = 0;
代码语言:txt
复制
        if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority;
代码语言:txt
复制
        if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload;
代码语言:txt
复制
        if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache;
代码语言:txt
复制
        if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground;
代码语言:txt
复制
        if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies;
代码语言:txt
复制
        if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates;
代码语言:txt
复制
        if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority;
代码语言:txt
复制
        if (options & SDWebImageScaleDownLargeImages) downloaderOptions |= SDWebImageDownloaderScaleDownLargeImages;
代码语言:txt
复制
        if (cachedImage && options & SDWebImageRefreshCached) {
代码语言:txt
复制
            // force progressive off if image already cached but forced refreshing
代码语言:txt
复制
            downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
代码语言:txt
复制
            // ignore image read from NSURLCache if image if cached but force refreshing
代码语言:txt
复制
            downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
代码语言:txt
复制
        }
代码语言:txt
复制
        //下载策略
代码语言:txt
复制
        SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
代码语言:txt
复制
            __strong __typeof(weakOperation) strongOperation = weakOperation;
代码语言:txt
复制
            if (!strongOperation || strongOperation.isCancelled) {
代码语言:txt
复制
                // Do nothing if the operation was cancelled
代码语言:txt
复制
                // See #699 for more details
代码语言:txt
复制
                // if we would call the completedBlock, there could be a race condition between this block and another completedBlock for the same object, so if this one is called second, we will overwrite the new data
代码语言:txt
复制
            } else if (error) {
代码语言:txt
复制
                [self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url];
代码语言:txt
复制
                if (   error.code != NSURLErrorNotConnectedToInternet
代码语言:txt
复制
                    && error.code != NSURLErrorCancelled
代码语言:txt
复制
                    && error.code != NSURLErrorTimedOut
代码语言:txt
复制
                    && error.code != NSURLErrorInternationalRoamingOff
代码语言:txt
复制
                    && error.code != NSURLErrorDataNotAllowed
代码语言:txt
复制
                    && error.code != NSURLErrorCannotFindHost
代码语言:txt
复制
                    && error.code != NSURLErrorCannotConnectToHost
代码语言:txt
复制
                    && error.code != NSURLErrorNetworkConnectionLost) {
代码语言:txt
复制
                    @synchronized (self.failedURLs) {
代码语言:txt
复制
                        [self.failedURLs addObject:url];
代码语言:txt
复制
                    }
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
            else {
代码语言:txt
复制
                if ((options & SDWebImageRetryFailed)) {
代码语言:txt
复制
                    @synchronized (self.failedURLs) {
代码语言:txt
复制
                        [self.failedURLs removeObject:url];
代码语言:txt
复制
                    }
代码语言:txt
复制
                }
代码语言:txt
复制
                BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
代码语言:txt
复制
                if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
代码语言:txt
复制
                    // Image refresh hit the NSURLCache cache, do not call the completion block
代码语言:txt
复制
                } else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
代码语言:txt
复制
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
代码语言:txt
复制
                        UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
代码语言:txt
复制
                        if (transformedImage && finished) {
代码语言:txt
复制
                            BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
代码语言:txt
复制
                            // pass nil if the image was transformed, so we can recalculate the data from the image
代码语言:txt
复制
                            [self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];
代码语言:txt
复制
                        }
代码语言:txt
复制
                        [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
代码语言:txt
复制
                    });
代码语言:txt
复制
                } else {
代码语言:txt
复制
                    if (downloadedImage && finished) {
代码语言:txt
复制
                        [self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
代码语言:txt
复制
                    }
代码语言:txt
复制
                    [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
            if (finished) {
代码语言:txt
复制
                [self safelyRemoveOperationFromRunning:strongOperation];
代码语言:txt
复制
            }
代码语言:txt
复制
        }];
代码语言:txt
复制
        operation.cancelBlock = ^{
代码语言:txt
复制
            [self.imageDownloader cancel:subOperationToken];
代码语言:txt
复制
            __strong __typeof(weakOperation) strongOperation = weakOperation;
代码语言:txt
复制
            [self safelyRemoveOperationFromRunning:strongOperation];
代码语言:txt
复制
        };
代码语言:txt
复制
    } else if (cachedImage) {
代码语言:txt
复制
        __strong __typeof(weakOperation) strongOperation = weakOperation;
代码语言:txt
复制
        [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
代码语言:txt
复制
        [self safelyRemoveOperationFromRunning:operation];
代码语言:txt
复制
    } else {
代码语言:txt
复制
        // Image not in cache and download disallowed by delegate
代码语言:txt
复制
        __strong __typeof(weakOperation) strongOperation = weakOperation;
代码语言:txt
复制
        [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
代码语言:txt
复制
        [self safelyRemoveOperationFromRunning:operation];
代码语言:txt
复制
    }
代码语言:txt
复制
}];
代码语言:txt
复制
return operation;

}

代码语言:txt
复制
- 其中,缓存策略调用为

```javascript

//缓存策略

operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage cachedImage, NSData cachedData, SDImageCacheType cacheType) {

...}

代码语言:txt
复制
- 下载策略调用为

```javascript

//下载策略

SDWebImageDownloadToken subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage downloadedImage, NSData downloadedData, NSError error, BOOL finished) {

...}

代码语言:txt
复制
## 4.缓存查询策略

- 类名

>  **SDImageCache.m**
>  

- 方法

>   
>  

...}

```javascript
  • 作用

1) 先查询 Memory Cache

UIImage *image = [self imageFromMemoryCacheForKey:key];

2) 如果 Memory Cache 查找不到, 就会查询 Disk Cache

`NSData diskData = self diskImageDataBySearchingAllPathsForKey:key; UIImage diskImage = self diskImageForKey:key;

`

3) 如果 Disk Cache 查询成功,还会把得到的图片再次设置到 Memory Cache 中。

[self.memCache setObject:diskImage forKey:key cost:cost];

4) 向block返回图片数据,如doneBlock(image, diskData, SDImageCacheTypeMemory);

其中,关于image,所有图均需返回 UIImage *image = [self imageFromMemoryCacheForKey:key];

关于diskData,如果为GIF图,需返回 diskData = [self diskImageDataBySearchingAllPathsForKey:key];,如果为非GIF图,则返回nil。

  • 实现- (nullable NSOperation \*)queryCacheOperationForKey:(nullable NSString \*)key done:(nullable SDCacheQueryCompletedBlock)doneBlock { if (!key) { if (doneBlock) { doneBlock(nil, nil, SDImageCacheTypeNone); } return nil; } // First check the in-memory cache... UIImage \*image = [self imageFromMemoryCacheForKey:key]; if (image) { NSData \*diskData = nil; if ([image isGIF]) { diskData = [self diskImageDataBySearchingAllPathsForKey:key]; } if (doneBlock) { doneBlock(image, diskData, SDImageCacheTypeMemory); } return nil; } NSOperation \*operation = [NSOperation new]; dispatch\_async(self.ioQueue, ^{ if (operation.isCancelled) { // do not call the completion if cancelled return; } @autoreleasepool { NSData \*diskData = [self diskImageDataBySearchingAllPathsForKey:key]; UIImage \*diskImage = [self diskImageForKey:key]; if (diskImage && self.config.shouldCacheImagesInMemory) { NSUInteger cost = SDCacheCostForImage(diskImage); [self.memCache setObject:diskImage forKey:key cost:cost]; } if (doneBlock) { dispatch\_async(dispatch\_get\_main\_queue(), ^{ doneBlock(diskImage, diskData, SDImageCacheTypeDisk); }); } } }); return operation; } ```javascript ## 5.下载管理策略
  • 类名

SDWebImageDownloader.h

  • 方法

代码语言:txt
复制
                                               options:(SDWebImageDownloaderOptions)options
代码语言:txt
复制
                                              progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
代码语言:txt
复制
                                             completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {

...}

代码语言:txt
复制
- 作用
- 利用Url建立Request

```javascript

NSMutableURLRequest *request = [NSMutableURLRequest alloc initWithURL:url cachePolicy:cachePolicy timeoutInterval:timeoutInterval];

代码语言:txt
复制
- 利用Request初始化SDWebImageDownloaderOperation下载对象

```javascript

SDWebImageDownloaderOperation *operation = [sself.operationClass alloc initWithRequest:request inSession:sself.session options:options];

代码语言:txt
复制
- 实现

```javascript
  • (nullable SDWebImageDownloadToken )downloadImageWithURL:(nullable NSURL )url options:(SDWebImageDownloaderOptions)options progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock { __weak SDWebImageDownloader *wself = self;
代码语言:txt
复制
return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
代码语言:txt
复制
    __strong __typeof (wself) sself = wself;
代码语言:txt
复制
    NSTimeInterval timeoutInterval = sself.downloadTimeout;
代码语言:txt
复制
    if (timeoutInterval == 0.0) {
代码语言:txt
复制
        timeoutInterval = 15.0;
代码语言:txt
复制
    }
代码语言:txt
复制
    // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
代码语言:txt
复制
    NSURLRequestCachePolicy cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
代码语言:txt
复制
    if (options & SDWebImageDownloaderUseNSURLCache) {
代码语言:txt
复制
        if (options & SDWebImageDownloaderIgnoreCachedResponse) {
代码语言:txt
复制
            cachePolicy = NSURLRequestReturnCacheDataDontLoad;
代码语言:txt
复制
        } else {
代码语言:txt
复制
            cachePolicy = NSURLRequestUseProtocolCachePolicy;
代码语言:txt
复制
        }
代码语言:txt
复制
    }
代码语言:txt
复制
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:cachePolicy timeoutInterval:timeoutInterval];
代码语言:txt
复制
    request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
代码语言:txt
复制
    request.HTTPShouldUsePipelining = YES;
代码语言:txt
复制
    if (sself.headersFilter) {
代码语言:txt
复制
        request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
代码语言:txt
复制
    }
代码语言:txt
复制
    else {
代码语言:txt
复制
        request.allHTTPHeaderFields = sself.HTTPHeaders;
代码语言:txt
复制
    }
代码语言:txt
复制
    SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
代码语言:txt
复制
    operation.shouldDecompressImages = sself.shouldDecompressImages;
代码语言:txt
复制
    if (sself.urlCredential) {
代码语言:txt
复制
        operation.credential = sself.urlCredential;
代码语言:txt
复制
    } else if (sself.username && sself.password) {
代码语言:txt
复制
        operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
代码语言:txt
复制
    }
代码语言:txt
复制
    if (options & SDWebImageDownloaderHighPriority) {
代码语言:txt
复制
        operation.queuePriority = NSOperationQueuePriorityHigh;
代码语言:txt
复制
    } else if (options & SDWebImageDownloaderLowPriority) {
代码语言:txt
复制
        operation.queuePriority = NSOperationQueuePriorityLow;
代码语言:txt
复制
    }
代码语言:txt
复制
    [sself.downloadQueue addOperation:operation];
代码语言:txt
复制
    if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
代码语言:txt
复制
        // Emulate LIFO execution order by systematically adding new operations as last operation's dependency
代码语言:txt
复制
        [sself.lastAddedOperation addDependency:operation];
代码语言:txt
复制
        sself.lastAddedOperation = operation;
代码语言:txt
复制
    }
代码语言:txt
复制
    return operation;
代码语言:txt
复制
}];

}

代码语言:txt
复制
## 6.缓存管理策略

## 6.1 自动化缓存管理

- 类名

>  SDImageCacheConfig.h
>  

- 属性

```javascript

/**

  • The maximum length of time to keep an image in the cache, in seconds */ @property (assign, nonatomic) NSInteger maxCacheAge;

/**

  • The maximum size of the cache, in bytes. */ @property (assign, nonatomic) NSUInteger maxCacheSize;- 类名 > SDImageCacheConfig.m > - 实现 ```javascript static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week

@implementation SDImageCacheConfig

  • (instancetype)init { if (self = super init) { _shouldDecompressImages = YES; _shouldDisableiCloud = YES; _shouldCacheImagesInMemory = YES; _maxCacheAge = kDefaultCacheMaxCacheAge; _maxCacheSize = 0; } return self; }

@end

代码语言:txt
复制
- 类名

>  SDImageCache.m
>  

- 添加通知

```javascript

#if SD_UIKIT

代码语言:txt
复制
    // Subscribe to app events
代码语言:txt
复制
    [[NSNotificationCenter defaultCenter] addObserver:self
代码语言:txt
复制
                                             selector:@selector(clearMemory)
代码语言:txt
复制
                                                 name:UIApplicationDidReceiveMemoryWarningNotification
代码语言:txt
复制
                                               object:nil];
代码语言:txt
复制
    [[NSNotificationCenter defaultCenter] addObserver:self
代码语言:txt
复制
                                             selector:@selector(deleteOldFiles)
代码语言:txt
复制
                                                 name:UIApplicationWillTerminateNotification
代码语言:txt
复制
                                               object:nil];
代码语言:txt
复制
    [[NSNotificationCenter defaultCenter] addObserver:self
代码语言:txt
复制
                                             selector:@selector(backgroundDeleteOldFiles)
代码语言:txt
复制
                                                 name:UIApplicationDidEnterBackgroundNotification
代码语言:txt
复制
                                               object:nil];

#endif

代码语言:txt
复制
- 内存警告:清除所有内存

```javascript
  • (void)clearMemory { self.memCache removeAllObjects; }- 程序结束:删除老文件(缩减一半) ```javascript if (self.config.maxCacheSize > 0 && currentCacheSize > self.config.maxCacheSize) { // Target half of our maximum cache size for this cleanup pass. const NSUInteger desiredCacheSize = self.config.maxCacheSize / 2;
代码语言:txt
复制
        // Sort the remaining cache files by their last modification time (oldest first).
代码语言:txt
复制
        NSArray<NSURL *> *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
代码语言:txt
复制
                                                                 usingComparator:^NSComparisonResult(id obj1, id obj2) {
代码语言:txt
复制
                                                                     return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
代码语言:txt
复制
                                                                 }];
代码语言:txt
复制
        // Delete files until we fall below our desired cache size.
代码语言:txt
复制
        for (NSURL *fileURL in sortedFiles) {
代码语言:txt
复制
            if ([_fileManager removeItemAtURL:fileURL error:nil]) {
代码语言:txt
复制
                NSDictionary<NSString *, id> *resourceValues = cacheFiles[fileURL];
代码语言:txt
复制
                NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
代码语言:txt
复制
                currentCacheSize -= totalAllocatedSize.unsignedIntegerValue;
代码语言:txt
复制
                if (currentCacheSize < desiredCacheSize) {
代码语言:txt
复制
                    break;
代码语言:txt
复制
                }
代码语言:txt
复制
            }
代码语言:txt
复制
        }
代码语言:txt
复制
    }
代码语言:txt
复制
- 进入后台:清除一半缓存并结束后台任务

```javascript

#if SD_UIKIT

  • (void)backgroundDeleteOldFiles { Class UIApplicationClass = NSClassFromString(@"UIApplication"); if(!UIApplicationClass || undefined) { return; } UIApplication *application = UIApplication performSelector:@selector(sharedApplication); __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{ // Clean up any unfinished task business by marking where you // stopped or ending the task outright. application endBackgroundTask:bgTask; bgTask = UIBackgroundTaskInvalid; }];
代码语言:txt
复制
// Start the long-running task and return immediately.
代码语言:txt
复制
[self deleteOldFilesWithCompletionBlock:^{
代码语言:txt
复制
    [application endBackgroundTask:bgTask];
代码语言:txt
复制
    bgTask = UIBackgroundTaskInvalid;
代码语言:txt
复制
}];

}

#endif

代码语言:txt
复制
#### 6.2 手动化内存管理

- 手动设置最大缓存空间

```javascript

//调用处获取单例进行设置

SDImageCache sharedImageCache.config.maxCacheSize = 1024 1024 50; // 50M

代码语言:txt
复制
- 手动设置缓存清理周期

```javascript

//SDImageCacheConfig.m

static const NSInteger kDefaultCacheMaxCacheAge = 60 60 24 * 7; // 1 week

代码语言:txt
复制
- 手动清除缓存

```javascript
  • (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion { dispatch_async(self.ioQueue, ^{ _fileManager removeItemAtPath:self.diskCachePath error:nil; _fileManager createDirectoryAtPath:self.diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL;
代码语言:txt
复制
    if (completion) {
代码语言:txt
复制
        dispatch_async(dispatch_get_main_queue(), ^{
代码语言:txt
复制
            completion();
代码语言:txt
复制
        });
代码语言:txt
复制
    }
代码语言:txt
复制
});

}

代码语言:txt
复制

小结

学习最好的方式,是自己研读里面的源码,当然限于篇幅,本篇文章介绍的重点是缓存策略,而对SDWebImage的网络管理部分一提而过。如果你愿意深入研究,你会发现更多层的调用栈和线程管理的操作。下载官方源码SDWebImage,你可以研究更多。

官方下载与说明

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.06.29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.框架简介
  • 2.官方Demo调用实例解析
    • 3.2 第二层
    • 3.4 第四层
    • 小结
      • 官方下载与说明
      相关产品与服务
      图片处理
      图片处理(Image Processing,IP)是由腾讯云数据万象提供的丰富的图片处理服务,广泛应用于腾讯内部各产品。支持对腾讯云对象存储 COS 或第三方源的图片进行处理,提供基础处理能力(图片裁剪、转格式、缩放、打水印等)、图片瘦身能力(Guetzli 压缩、AVIF 转码压缩)、盲水印版权保护能力,同时支持先进的图像 AI 功能(图像增强、图像标签、图像评分、图像修复、商品抠图等),满足多种业务场景下的图片处理需求。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档