SDWebImage学习

SDWebImage版本是:'4.2.2'

SDWebImage是iOS开发中常用的图片加载的库,能下载并缓存图片。这次就着重介绍SDWebImage的特色功能:下载与缓存。

UIImageView+WebCache:直接使用的类

SDWebImageManager:总的管理类,维护了SDWebImageDownloader和SDImageCache实例,是下载与缓存的桥梁

SDWebImageDownloader:图片的下载队列

SDWebImageDownloaderOperation:真正的图片下载请求

SDImageCache:图片的缓存

SDWebImage的大体流程:

下面介绍一下SDWebImage的下载及缓存思路:

下载

在SDWebImage中,图片的下载是由SDWebImageDownloader类来完成。

UIImageView +WebCache提供的接口方法中,真正进行下载逻辑的是

// ==============  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;

而这个方法是UIView+WebCache分类中的。由于SDWebImage框架支持UIButton的下载图片,所以把下载方法放到父类UIView中最为合适。

SDWebImageManager

维护了SDWebImageDownloader和SDImageCache实例,是下载与缓存的桥梁。

在下载任务开始的时候,SDWebImageManager首选会去SDImageCache查询是否有缓存,有了就直接返回,没有就去SDWebImageDownloader进行下载,各司其职。

SDWebImageManager有这几个重要的属性

@property (strong, nonatomic, readwrite, nonnull) SDImageCache imageCache;//管理缓存

@property (strong, nonatomic, readwrite, nonnull) SDWebImageDownloader //下载器
imageDownloader;

@property (strong, nonatomic, nonnull) NSMutableSet<NSURL *> *failedURLs;//记录失效url的名单

@property (strong, nonatomic, nonnull) NSMutableArray<SDWebImageCombinedOperation *> *runningOperations;//记录当前正在执行的操作

SDWebImageManager下载的入口方法:loadImageWithURL:options:progress:completed:

下面是他的实现:

- (id <SDWebImageOperation>)loadImageWithURL2:(nullable NSURL *)url
options:(SDWebImageOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDInternalCompletionBlock)completedBlock {
... //在SDImageCache里查询是否存在缓存的图片
operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
if (operation.isCancelled) {
[self safelyRemoveOperationFromRunning:operation];
return;
}
//没有缓存图片
if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
//没有图片,刷新缓存
if (cachedImage && options & SDWebImageRefreshCached) {
[self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
} ... //通过SDWebImageDownloader开始下载
SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
__strong __typeof(weakOperation) strongOperation = weakOperation;
if (!strongOperation || strongOperation.isCancelled) {
} else if (error) {
[self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url]; //如果出错,就添加到下载错误的failedURLs数组中
if (error.code != NSURLErrorNotConnectedToInternet && ...) {
@synchronized (self.failedURLs) {
[self.failedURLs addObject:url];
}
}
}
else {
if ((options & SDWebImageRetryFailed)) {
@synchronized (self.failedURLs) {
[self.failedURLs removeObject:url];
}
} //进行缓存
BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly); if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
// Image refresh hit the NSURLCache cache, do not call the completion block
} else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]; if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
// pass nil if the image was transformed, so we can recalculate the data from the image
[self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];
} [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
});
} else {
//下载成功,结束
if (downloadedImage && finished) {
[self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
}
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
}
} if (finished) {
[self safelyRemoveOperationFromRunning:strongOperation];
}
}];
@synchronized(operation) {
operation.cancelBlock = ^{
[self.imageDownloader cancel:subOperationToken];
__strong __typeof(weakOperation) strongOperation = weakOperation;
[self safelyRemoveOperationFromRunning:strongOperation];
};
}
} else if (cachedImage) {
__strong __typeof(weakOperation) strongOperation = weakOperation;
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
[self safelyRemoveOperationFromRunning:operation];
} else {
// Image not in cache and download disallowed by delegate
__strong __typeof(weakOperation) strongOperation = weakOperation;
[self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
[self safelyRemoveOperationFromRunning:operation];
}
}]; return operation;
}
SDWebImageDownloader

SDWebImageDownloader下载管理器是一个单例类,它主要负责图片的下载操作的管理,有下面几个关键属性:

@property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations;
@property (strong, nonatomic) NSOperationQueue *downloadQueue; //下载队列
@property (SDDispatchQueueSetterSementics, nonatomic) dispatch_queue_t barrierQueue;

downloadQueue: 来管理下载队列的,改队列在子线程中异步执行

barrierQueue: 是一个串行队列,在一个单一队列中顺序处理所有下载操作的网络响应,由于允许多个图片同时下载,因此可能会有多个线程同时操作URLCallbacks属性。为了保证URLCallbacks操作(添加、删除)的线程安全性,SDWebImageDownloader将这些操作作为一个个任务放到barrierQueue队列中。

URLOperations:是NSMutableDictionary来防止下载未完成时,重复添加到下载队列中

既然是管理类,那么下载开始,暂停,取消都是支持的,分别是以下的方法。

- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock; - (void)cancel:(nullable SDWebImageDownloadToken *)token; - (void)setSuspended:(BOOL)suspended; - (void)cancelAllDownloads;

下载:入口方法:downloadImageWithURL:options:progress:completed:,方法内部通过SDWebImageDownloaderOperation来进行真正的下载。下载来时先到URLOperations字典中看有没有已经存在的下载操作,没有才进行创建SDWebImageDownloaderOperation添加到URLOperations中,并添加到downloadQueue进行队列管理。

取消单个下载:根据url取出SDWebImageDownloaderOperation,并执行对应的取消方法:- (BOOL)cancel:(nullable id)token;,然后把操作移出字典URLOperations

取消全部:直接调用队列downloadQueuecancelAllOperations取消全部的方法。

暂停:设置self.downloadQueue.suspended状态。

SDWebImageDownloaderOperation

SDWebImageDownloaderOperation类,它继承自NSOperation,,通过NSURLSession的子类NSURLSessionDataTask下载图片,这是iOS新的Api,老版的是通过NSURLConnection(iOS7.0以前可用)下载的。如果设置了后台也进行下载SDWebImageDownloaderContinueInBackground那么会通过UIBackgroundTaskIdentifier来进行相关设置。

//TODO

缓存

SDWebImage提供了对图片缓存的支持,这样下次再去获取同一张图片时,可以直接从本地获取,而不再从远程服务器获取。而该功能是由SDImageCache类来完成的。

SDImageCache包含了内存缓存和磁盘缓存。:内存缓存和磁盘缓存。内存缓存由AutoPurgeCache(NSCache的子类)管理。磁盘缓存是NSFileManager来管理。

SDImageCache定义了一个串行队列dispatch_queue_t ioQueue,来异步存储图片。

存储方法:

- (void)storeImage:(nullable UIImage *)image forKey:(nullable NSString *)key completion:(nullable SDWebImageNoParamsBlock)completionBlock;

- (void)storeImage:(nullable UIImage *)image forKey:(nullable NSString *)key toDisk:(BOOL)toDisk completion:(nullable SDWebImageNoParamsBlock)completionBlock;

- (void)storeImage:(nullable UIImage *)image imageData:(nullable NSData *)imageData forKey:(nullable NSString *)key toDisk:(BOOL)toDisk completion:(nullable SDWebImageNoParamsBlock)completionBlock;

- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;

存储图片时以图片Url的MD5值作为key进行存储,存储的基础方法是- (void)storeImage: imageData: forKey: toDisk: completion:实现如下:如果设置是需要缓存在内存,就缓存在内存中,再就是存储在沙盒中。

查询图片

在内存或磁盘中查询是否有key指定的图片,则可以分别使用以下方法:

- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;

- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;

- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;

- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;

- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;

删除图片

缓存的删除主要分成两种:单个和全部

通过key来删除单张图片

下面该方法是主要的方法,通过fromDisk的值来决定删除内存缓存还是删除所有的缓存(包括内存和沙盒)。通过completion来完成删除后的事项。

其中clearMemory是清理内存缓存,系统注册了UIApplicationDidReceiveMemoryWarningNotification通知,在内存警告的时候,会主动清理内存缓存。而方法- (void)clearDiskOnCompletion:则是根据缓存的有效期以及最大的缓存大小进行清理。缓存有效期:根据maxCacheAge设置,默认为一周;最大缓存空间大小:根据maxCacheSize设置。缓存总大小超过这个值得时候,就会根据文件文件最后修改的时间倒序来进行移除。

#pragma mark - Remove Ops
- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion; - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion; #pragma mark - Cache clean Ops
- (void)clearMemory;
- (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion; - (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;

//TODO

总结

学习SDWebImage能学到:

  1. dispatch_barrier_sync函数:确保在执行完任务后才会执行后续操作。该方法常用于确保类的线程安全性操作
  2. @synchronized:多线程访问数组,保证同一时间只有一个线程在操作
  3. NSMutableURLRequest:用于创建一个网络请求对象,我们可以根据需要来配置请求报头等信息。
  4. NSOperation及NSOperationQueue:操作队列是Objective-C中一种高级的并发处理方法,它是基于GCD来实现的。相对于GCD来说,操作队列的优点是可以取消在任务处理队列中的任务,另外在管理操作间的依赖关系方面也容易一些。对SDWebImage中我们就看到了如何使用依赖将下载顺序设置成后进先出的顺序
  5. NSURLSession:用于网络请求及响应处理。是苹果在iOS7.0后推出了一套新的网络请求接口
  6. 开启一个后台任务。
  7. NSCache类:一个类似于集合的容器。它存储key-value对,这一点类似于NSDictionary类。我们通常用使用缓存来临时存储短时间使用但创建昂贵的对象。重用这些对象可以优化性能,因为它们的值不需要重新计算。另外一方面,这些对象对于程序来说不是紧要的,在内存紧张时会被丢弃。
  8. 清理缓存图片的策略:特别是最大缓存空间大小的设置。如果所有缓存文件的总大小超过这一大小,则会按照文件最后修改时间的逆序,以每次一半的递归来移除那些过早的文件,直到缓存的实际大小小于我们设置的最大使用空间。

最新文章

  1. handlebars自定义helper的写法
  2. DB2 中文排序问题
  3. python文件头的#-*- coding: utf-8 -*- 的作用
  4. node.js整理 07例子
  5. archlinux pacman 常用选项
  6. vs2015 配置opencv3.0遇到的问题
  7. eclipse-SDK-3.7-win32;eclipse-java-indigo-win32;eclipse-jee-indigo-win32 区别(ZZ)
  8. 使用getGenericSuperclass()和getActualTypeArguments()将DAO做成泛型
  9. shell脚本加密
  10. go web 第三天 学习笔记 --mysql
  11. HDU 1312 Red and Black(DFS,板子题,详解,零基础教你代码实现DFS)
  12. SOFARPC源码解析-搭建环境
  13. 数据库H2学习
  14. 数据结构与算法JS实现
  15. WEB学习笔记4-前端代码基本命名规法和格式规范
  16. jmeter本身的一个bug记录
  17. Linux之命令进阶
  18. PAT 1039 到底买不买(20)(20 分)
  19. 20155207 EXP8 Web基础
  20. 混沌分形之谢尔宾斯基(Sierpinski)

热门文章

  1. linux maven安装(三)
  2. Linux运维打怪升级篇,从苦逼到牛逼的必备装备(转)
  3. LeetCode OJ——Word Ladder2
  4. win10 升级导致找不到SQL Server配置管理器
  5. 微信小程序踩坑之一[thist]使用技巧
  6. Codeforces Gym101502 H.Eyad and Math-换底公式
  7. Codeforces Gym101502 B.Linear Algebra Test-STL(map)
  8. linux环境设置export
  9. k8s的使用入门
  10. PyTorch学习问题记录