SDWebImage学习

SDWebImage版本是:'4.2.2'

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

UIImageView+WebCache:直接使用的类

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

SDWebImageDownloader:图片的下载队列

SDWebImageDownloaderOperation:真正的图片下载请求

SDImageCache:图片的缓存

SDWebImage的大体流程:

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

下载

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

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

  1. // ============== UIView+ WebCache.m ============== //
  2. - (void)sd_internalSetImageWithURL:(nullable NSURL *)url
  3. placeholderImage:(nullable UIImage *)placeholder
  4. options:(SDWebImageOptions)options
  5. operationKey:(nullable NSString *)operationKey
  6. setImageBlock:(nullable SDSetImageBlock)setImageBlock
  7. progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
  8. 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:

下面是他的实现:

  1. - (id <SDWebImageOperation>)loadImageWithURL2:(nullable NSURL *)url
  2. options:(SDWebImageOptions)options
  3. progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
  4. completed:(nullable SDInternalCompletionBlock)completedBlock {
  5. ...
  6. //在SDImageCache里查询是否存在缓存的图片
  7. operation.cacheOperation = [self.imageCache queryCacheOperationForKey:key done:^(UIImage *cachedImage, NSData *cachedData, SDImageCacheType cacheType) {
  8. if (operation.isCancelled) {
  9. [self safelyRemoveOperationFromRunning:operation];
  10. return;
  11. }
  12. //没有缓存图片
  13. if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
  14. //没有图片,刷新缓存
  15. if (cachedImage && options & SDWebImageRefreshCached) {
  16. [self callCompletionBlockForOperation:weakOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
  17. }
  18. ...
  19. //通过SDWebImageDownloader开始下载
  20. SDWebImageDownloadToken *subOperationToken = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *downloadedData, NSError *error, BOOL finished) {
  21. __strong __typeof(weakOperation) strongOperation = weakOperation;
  22. if (!strongOperation || strongOperation.isCancelled) {
  23. } else if (error) {
  24. [self callCompletionBlockForOperation:strongOperation completion:completedBlock error:error url:url];
  25. //如果出错,就添加到下载错误的failedURLs数组中
  26. if (error.code != NSURLErrorNotConnectedToInternet && ...) {
  27. @synchronized (self.failedURLs) {
  28. [self.failedURLs addObject:url];
  29. }
  30. }
  31. }
  32. else {
  33. if ((options & SDWebImageRetryFailed)) {
  34. @synchronized (self.failedURLs) {
  35. [self.failedURLs removeObject:url];
  36. }
  37. }
  38. //进行缓存
  39. BOOL cacheOnDisk = !(options & SDWebImageCacheMemoryOnly);
  40. if (options & SDWebImageRefreshCached && cachedImage && !downloadedImage) {
  41. // Image refresh hit the NSURLCache cache, do not call the completion block
  42. } else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) {
  43. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
  44. UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url];
  45. if (transformedImage && finished) {
  46. BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
  47. // pass nil if the image was transformed, so we can recalculate the data from the image
  48. [self.imageCache storeImage:transformedImage imageData:(imageWasTransformed ? nil : downloadedData) forKey:key toDisk:cacheOnDisk completion:nil];
  49. }
  50. [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:transformedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
  51. });
  52. } else {
  53. //下载成功,结束
  54. if (downloadedImage && finished) {
  55. [self.imageCache storeImage:downloadedImage imageData:downloadedData forKey:key toDisk:cacheOnDisk completion:nil];
  56. }
  57. [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:downloadedImage data:downloadedData error:nil cacheType:SDImageCacheTypeNone finished:finished url:url];
  58. }
  59. }
  60. if (finished) {
  61. [self safelyRemoveOperationFromRunning:strongOperation];
  62. }
  63. }];
  64. @synchronized(operation) {
  65. operation.cancelBlock = ^{
  66. [self.imageDownloader cancel:subOperationToken];
  67. __strong __typeof(weakOperation) strongOperation = weakOperation;
  68. [self safelyRemoveOperationFromRunning:strongOperation];
  69. };
  70. }
  71. } else if (cachedImage) {
  72. __strong __typeof(weakOperation) strongOperation = weakOperation;
  73. [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:cachedImage data:cachedData error:nil cacheType:cacheType finished:YES url:url];
  74. [self safelyRemoveOperationFromRunning:operation];
  75. } else {
  76. // Image not in cache and download disallowed by delegate
  77. __strong __typeof(weakOperation) strongOperation = weakOperation;
  78. [self callCompletionBlockForOperation:strongOperation completion:completedBlock image:nil data:nil error:nil cacheType:SDImageCacheTypeNone finished:YES url:url];
  79. [self safelyRemoveOperationFromRunning:operation];
  80. }
  81. }];
  82. return operation;
  83. }
SDWebImageDownloader

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

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

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

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

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

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

  1. - (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
  2. options:(SDWebImageDownloaderOptions)options
  3. progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
  4. completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
  5. - (void)cancel:(nullable SDWebImageDownloadToken *)token;
  6. - (void)setSuspended:(BOOL)suspended;
  7. - (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,来异步存储图片。

存储方法:

  1. - (void)storeImage:(nullable UIImage *)image forKey:(nullable NSString *)key completion:(nullable SDWebImageNoParamsBlock)completionBlock;
  2. - (void)storeImage:(nullable UIImage *)image forKey:(nullable NSString *)key toDisk:(BOOL)toDisk completion:(nullable SDWebImageNoParamsBlock)completionBlock;
  3. - (void)storeImage:(nullable UIImage *)image imageData:(nullable NSData *)imageData forKey:(nullable NSString *)key toDisk:(BOOL)toDisk completion:(nullable SDWebImageNoParamsBlock)completionBlock;
  4. - (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;

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

查询图片

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

  1. - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
  2. - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
  3. - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;
  4. - (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;
  5. - (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;

删除图片

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

通过key来删除单张图片

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

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

  1. #pragma mark - Remove Ops
  2. - (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
  3. - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;
  4. #pragma mark - Cache clean Ops
  5. - (void)clearMemory;
  6. - (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;
  7. - (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. 清理缓存图片的策略:特别是最大缓存空间大小的设置。如果所有缓存文件的总大小超过这一大小,则会按照文件最后修改时间的逆序,以每次一半的递归来移除那些过早的文件,直到缓存的实际大小小于我们设置的最大使用空间。

SDWebImage学习的更多相关文章

  1. SDWebImage学习之 NSCache

    1.使用SDWebImage的好处 1.异步下载(避免主线程卡死) 2.做好图片缓存(这样就不需要每次都加载网络图片) 3.解决了循环利用的问题 很容易造成内存警告

  2. iOS燃烧动画、3D视图框架、天气动画、立体相册、微信朋友圈小视频等源码

    iOS精选源码 iOS天气动画,包括太阳,云,雨,雷暴,雪动画. 较为美观的多级展开列表 3D立体相册,可以旋转的立方体 一个仪表盘Demo YGDashboardView 一个基于UIScrollV ...

  3. (20160602)开源第三方学习之SDWebImage

    这个类库提供一个UIImageView类别以支持加载来自网络的远程图片.具有缓存管理.异步下载.同一个URL下载次数控制和优化等特征. 地址:https://github.com/rs/SDWebIm ...

  4. SDWebImage第三方库学习

    1.基本使用方法 //异步下载并缓存 - (void)sd_setImageWithURL:(nullable NSURL *)url NS_REFINED_FOR_SWIFT; //使用占位图片,当 ...

  5. SDWebImage源码解读 之 NSData+ImageContentType

    第一篇 前言 从今天开始,我将开启一段源码解读的旅途了.在这里先暂时不透露具体解读的源码到底是哪些?因为也可能随着解读的进行会更改计划.但能够肯定的是,这一系列之中肯定会有Swift版本的代码. 说说 ...

  6. SDWebImage源码解读之SDWebImageCache(下)

    第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...

  7. 【原】SDWebImage源码阅读(五)

    [原]SDWebImage源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 前面的代码并没有特意去讲SDWebImage的缓存机制,主要是想单独开一章节专门讲 ...

  8. 【原】SDWebImage源码阅读(三)

    [原]SDWebImage源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1.SDWebImageDownloader中的downloadImageWithURL 我们 ...

  9. 【原】SDWebImage源码阅读(二)

    [原]SDWebImage源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 解决上一篇遗留的坑 上一篇中对sd_setImageWithURL函数简单分析了一下,还 ...

随机推荐

  1. 【前端学习笔记】2015-09-06 ~~~~ setAttribute()、slice()

    所遇记录: 1.setAttribute("属性",value),相同的还有addAttribute("属性名",value),getAttribute(“属性 ...

  2. scrapy的调试方法

    Parse命令,Scrapy shell,logging 一 Parse命令 检查spider输出的最基本方法是使用Parse命令.这能让你在函数层上检查spider哥哥部分的效果,其十分灵活并且已用 ...

  3. bzoj 3779 重组病毒 好题 LCT+dfn序+线段树分类讨论

    题目大意 1.将x到当前根路径上的所有点染成一种新的颜色: 2.将x到当前根路径上的所有点染成一种新的颜色,并且把这个点设为新的根: 3.查询以x为根的子树中所有点权值的平均值. 分析 原题codec ...

  4. 【NOIP2016练习】T1 挖金矿(二分答案)

    题意: 思路:二分答案A 合法的答案 sigma(s[i][xi])/sigma(xi)>=a i<=m sigma(s[i][xi]-a*xi)>=0 对于每个i找到xi使s[i] ...

  5. Memcached简单介绍

    Memcached简单介绍 简介:Memcached是一个自由开源的,高性能,分布式内存对象缓存系统.================================================= ...

  6. Yii CActiveForm 客户端验证(enableClientValidation)和自定义验证

    使用Yii的CActiveForm默认使用服务器端模型(model)的rules规则验证数据. 但这会导致无谓的请求提交,比较好的方式是为了用户体验在客户端也验证,而为了安全性,在服务器端和数据库也做 ...

  7. hdu 5019(第K大公约数)

    Revenge of GCD Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  8. linux jar 命令使用

    原文链接:http://blog.chinaunix.net/uid-692788-id-2681136.html JAR包是Java中所特有一种压缩文档,其实大家就可以把它理解为.zip包.当然也是 ...

  9. 唤醒你的大脑 --- javascript冒泡排序

    var a; a = [1, 2, 3, 11, 55, 5, 0, 44]; (function bubbleSort() { for (var i = 0; i <= a.length - ...

  10. openfire Android学习(二)----对分组、好友和头像等一些操作

    一.查询所有分组 通过Roster来获取所有分组,Roster可以通过connection.getRoster()来得到. [java] view plaincopy /** * 获取所有组 * *  ...