第五篇

前言

本篇主要讲解图片缓存类的知识,虽然只涉及了图片方面的缓存的设计,但思想同样适用于别的方面的设计。在架构上来说,缓存算是存储设计的一部分。我们把各种不同的存储内容按照功能进行切割后,图片缓存便是其中的一个。

我们在封装自己的图片缓存管理对象的时候,SDWebImageCache能够提供大约90%的代码给我们直接使用,基于这些代码,我们需要分析出作者的设计思想是什么?当需要缓存某个列表时,基于SDWebImageCache的设计思想,我们就能够设计出比较合理的缓存管理对象了。

所谓举一反三就是这样的道理。

整体架构

我们不看实现文件,只看作者暴露出来的内容,来分析该类有哪些属性和方法。看完整体架构这一节,我们必须明白如何使用这个缓存管理者。具体的实现过程会在下边的实现原理一节中讲解。

1.缓存位置

图片可以被缓存到两个地方:

  • 内存
  • 硬盘

2.配置

通过SDImageCacheConfig这个类来管理缓存的配置信息,我们打开SDImageCacheConfig后,发现可以配置的东西有:

  • shouldDecompressImages 是否解压缩图片,默认为YES
  • disable iCloud backup 是否禁用iCloud备份, 默认为YES
  • shouldCacheImagesInMemory 是否缓存到内存中,默认为YES
  • maxCacheAge 最大的缓存不过期时间, 单位为秒,默认为一周的时间
  • maxCacheSize 最大的缓存尺寸,单位为字节

代码如下:

@interface SDImageCacheConfig : NSObject

/**
* Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
* Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.
*/
@property (assign, nonatomic) BOOL shouldDecompressImages; /**
* disable iCloud backup [defaults to YES]
*/
@property (assign, nonatomic) BOOL shouldDisableiCloud; /**
* use memory cache [defaults to YES]
*/
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory; /**
* 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; @end

--

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

3.内存最大缓存

可以通过maxMemoryCost来设置内存的最大缓存是多少,这个是以像素为单位的。

4.最大内存缓存数量

可以通过maxMemoryCountLimit来设置内存的最大缓存数量是多少。

5.初始化

一般来说,一个管理类都有一个全局的单利对象,该类也不例外,然后根据业务需求设计不同的初始化方法。不管是什么样的类,我们在设计它的时候,应该通过合理的初始化方法告诉别的开发者,该类应该如何创建

  • + (nonnull instancetype)sharedImageCache 单利
  • - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns 通过制定的namespace来初始化
  • - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER 指定namespacepath.

注意:如果想设置某个方法为指定的初始化方法,通过NS_DESIGNATED_INITIALIZER来实现。

6.Cache paths

既然把数据缓存到了disk中,那么就要提供一个方法获取这个缓存路径。这里通过下边这个方法,根据namespace获取缓存路径:

- (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace;

注意:在开发中,我们会遇到这样的情况,假如我之前把图片缓存到了地址1,现在我打算重构代码。写了这么一个缓存管理者,我需要和之前的缓存的图片建立联系,但是以后都打算使用新写的这个管理者,那怎么办呢??

我们想到,我只需要把之前的路径添加到管理类的路径集合中就行了。主要目的是在搜索图片的时候,也有权限去搜索新添加的路径。

我在想,一个好的架构,或框架,应该使用这用思想

这也是下边这个方法的意义:

/**
* Add a read-only cache path to search for images pre-cached by SDImageCache
* Useful if you want to bundle pre-loaded images with your app
*
* @param path The path to use for this read-only cache path
*/
- (void)addReadOnlyCachePath:(nonnull NSString *)path;

7.存储图片

我们已经说过了,图片会被存储到内存或者硬盘中,在这一存储过程的设计中有下边这几个需要考虑的因素:

  • 数据源:可以保存UIImage也可以保存NSData
  • 唯一标识:找到该数据的唯一标识,一般使用图片的URL
  • 是否需要保存到硬盘:根据配置文件中的设置,如果设置了应该缓存到内存,那么图片肯定会被缓存到内存中。
  • 数据保存这一过程必须是异步的,在完成之后,在主线程回调

代码如下:

/**
* Asynchronously store an image into memory and disk cache at the given key.
*
* @param image The image to store
* @param key The unique image cache key, usually it's image absolute URL
* @param completionBlock A block executed after the operation is finished
*/
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
completion:(nullable SDWebImageNoParamsBlock)completionBlock; /**
* Asynchronously store an image into memory and disk cache at the given key.
*
* @param image The image to store
* @param key The unique image cache key, usually it's image absolute URL
* @param toDisk Store the image to disk cache if YES
* @param completionBlock A block executed after the operation is finished
*/
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock; /**
* Asynchronously store an image into memory and disk cache at the given key.
*
* @param image The image to store
* @param imageData The image data as returned by the server, this representation will be used for disk storage
* instead of converting the given image object into a storable/compressed image format in order
* to save quality and CPU
* @param key The unique image cache key, usually it's image absolute URL
* @param toDisk Store the image to disk cache if YES
* @param completionBlock A block executed after the operation is finished
*/
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock; /**
* Synchronously store image NSData into disk cache at the given key.
*
* @warning This method is synchronous, make sure to call it from the ioQueue
*
* @param imageData The image data to store
* @param key The unique image cache key, usually it's image absolute URL
*/
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key;

8.获取图片

对于如何获取图片。作者给出了比较多的方式,首先考虑内存和硬盘,其次考虑异步获取还是同步获取。如果获取数据异步的,就要使用block。总结下来有这么几种情况:

  • 判断图片是否被缓存到disk(异步)

      /**
    * Async check if image exists in disk cache already (does not load the image)
    *
    * @param key the key describing the url
    * @param completionBlock the block to be executed when the check is done.
    * @note the completion block will be always executed on the main queue
    */
    - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
  • 异步查询图片是否存在,这里返回了一个NSOperation,原因是在内存中获取耗时非常短,在disk中时间相对较长。

      /**
    * Operation that queries the cache asynchronously and call the completion when done.
    *
    * @param key The unique key used to store the wanted image
    * @param doneBlock The completion block. Will not get called if the operation is cancelled
    *
    * @return a NSOperation instance containing the cache op
    */
    - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
  • 同步在内存查询图片

      /**
    * Query the memory cache synchronously.
    *
    * @param key The unique key used to store the image
    */
    - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;
  • 同步在disk查询图片

      /**
    * Query the disk cache synchronously.
    *
    * @param key The unique key used to store the image
    */
    - (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;
  • 同步查找图片,先内存后disk

      /**
    * Query the cache (memory and or disk) synchronously after checking the memory cache.
    *
    * @param key The unique key used to store the image
    */
    - (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;

9.移除某条数据

数据可能存在于内存,也可能是disk,也可能两者都有,那么我们要想移除数据,就要考虑这些情况了。

  • 全部移除

      /**
    * Remove the image from memory and disk cache asynchronously
    *
    * @param key The unique image cache key
    * @param completion A block that should be executed after the image has been removed (optional)
    */
    - (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
  • 移除内存数据,是否也移除disk数据

      /**
    * Remove the image from memory and optionally disk cache asynchronously
    *
    * @param key The unique image cache key
    * @param fromDisk Also remove cache entry from disk if YES
    * @param completion A block that should be executed after the image has been removed (optional)
    */
    - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;
  • 移除disk数据,是否也移除内存数据 这种情况SDWebImageCache未实现

10.移除数据

这个移除不同于上边的移除,它会清空所有的符合条件的数据。

  • 清空内存

      /**
    * Clear all memory cached images
    */
    - (void)clearMemory;
  • 清空disk

      /**
    * Async clear all disk cached images. Non-blocking method - returns immediately.
    * @param completion A block that should be executed after cache expiration completes (optional)
    */
    - (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;
  • 清空过期数据

      /**
    * Async remove all expired cached image from disk. Non-blocking method - returns immediately.
    * @param completionBlock A block that should be executed after cache expiration completes (optional)
    */
    - (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;

11.获取缓存相关信息

获取缓存的相关信息:

  • 获取disk使用size

      /**
    * Get the size used by the disk cache
    */
    - (NSUInteger)getSize;
  • 获取disk缓存的图片数目

      /**
    * Get the number of images in the disk cache
    */
    - (NSUInteger)getDiskCount;
  • 异步获取disk使用size

      /**
    * Asynchronously calculate the disk cache's size.
    */
    - (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock;
  • 获取某个路径下的指定的图片,比如key为http://www.123.com/image.png,path为http://www.456.com,那么调用后边的方法后,返回http://www.456.com/image.png

      /**
    * Get the cache path for a certain key (needs the cache path root folder)
    *
    * @param key the key (can be obtained from url using cacheKeyForURL)
    * @param path the cache path root folder
    *
    * @return the cache path
    */
    - (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path;
  • 获取默认的缓存路径

      /**
    * Get the default cache path for a certain key
    *
    * @param key the key (can be obtained from url using cacheKeyForURL)
    *
    * @return the default cache path
    */
    - (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key;

总结

本来打算把实现部分也写到这篇文章的,但是现在看来不太合适,文章太长了,影响阅读体验。阅读完本篇后,我们就能够明白SDWebImageCache究竟能够给我提供哪些功能,更进一步,我们了解到设计这样一个管理者的答题思路是什么。下一篇就是该管理者的实现部分。

由于个人知识有限,如有错误之处,还望各路大侠给予指出啊

  1. SDWebImage源码解读 之 NSData+ImageContentType 简书 博客园
  2. SDWebImage源码解读 之 UIImage+GIF 简书 博客园
  3. SDWebImage源码解读 之 SDWebImageCompat 简书 博客园
  4. SDWebImage源码解读_之SDWebImageDecoder 简书 博客园

SDWebImage源码解读之SDWebImageCache(上)的更多相关文章

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

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

  2. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  3. SDWebImage源码解读之SDWebImageDownloader

    SDWebImage源码解读之SDWebImageDownloader 第八篇 前言 SDWebImageDownloader这个类非常简单,作者的设计思路也很清晰,但是我想在这说点题外话. 如果有人 ...

  4. SDWebImage源码解读之SDWebImageManager

    第九篇 前言 SDWebImageManager是SDWebImage中最核心的类了,但是源代码确是非常简单的.之所以能做到这一点,都归功于功能的良好分类. 有了SDWebImageManager这个 ...

  5. SDWebImage源码解读之SDWebImagePrefetcher

    > 第十篇 ## 前言 我们先看看`SDWebImage`主文件的组成模块: ![](http://images2015.cnblogs.com/blog/637318/201701/63731 ...

  6. SDWebImage源码解读之分类

    第十一篇 前言 我们知道SDWebImageManager是用来管理图片下载的,但我们平时的开发更多的是使用UIImageView和UIButton这两个控件显示图片. 按照正常的想法,我们只需要在他 ...

  7. SDWebImage源码解读之干货大总结

    这是我认为的一些重要的知识点进行的总结. 1.图片编码简介 大家都知道,数据在网络中是以二进制流的形式传播的,那么我们该如何把那些1和0解析成我们需要的数据格式呢? 说的简单一点就是,当文件都使用二进 ...

  8. SDWebImage源码解读 之 UIImage+GIF

    第二篇 前言 本篇是和GIF相关的一个UIImage的分类.主要提供了三个方法: + (UIImage *)sd_animatedGIFNamed:(NSString *)name ----- 根据名 ...

  9. SDWebImage源码解读 之 SDWebImageCompat

    第三篇 前言 本篇主要解读SDWebImage的配置文件.正如compat的定义,该配置文件主要是兼容Apple的其他设备.也许我们真实的开发平台只有一个,但考虑各个平台的兼容性,对于框架有着很重要的 ...

随机推荐

  1. Apache Ignite高性能分布式网格框架-初探

    Apache Ignite初步认识 今年4月开始倒腾openfire,过程中经历了许多,更学到了许多.特别是在集群方面有了很多的认识,真正开始认识到集群的概念及应用方法. 在openfire中使用的集 ...

  2. Http状态码之:301、302重定向

    概念 301 Moved Permanently 被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个URI之一.如果可能,拥有链接编辑功能的客户端应当自动把请求的地 ...

  3. Taurus.MVC 2.2 开源发布:WebAPI 功能增强(请求跨域及Json转换)

    背景: 1:有用户反馈了关于跨域请求的问题. 2:有用户反馈了参数获取的问题. 3:JsonHelper的增强. 在综合上面的条件下,有了2.2版本的更新,也因此写了此文. 开源地址: https:/ ...

  4. Gradle配置APK自动签名完整流程

    转载请注明出处:http://www.cnblogs.com/LT5505/p/6256683.html 一.生成签名 1.命令行生成签名,输入命令keytool -genkey -v -keysto ...

  5. ajax

    常见的HTTP状态码状态码:200 请求成功.一般用于GET和POST方法 OK301 资源移动.所请求资源移动到新的URL,浏览器自动跳转到新的URL Moved Permanently304 未修 ...

  6. Partition2:对表分区

    在SQL Server中,普通表可以转化为分区表,而分区表不能转化为普通表,普通表转化成分区表的过程是不可逆的,将普通表转化为分区表的方法是: 在分区架构(Partition Scheme)上创建聚集 ...

  7. linux centos中添加删除修改环境变量,设置java环境变量

    前言 安装完软件必要添加环境变量.指令很少,然而长时间不写就会不自信:我写的对吗?于是百度开始,于是发现又是各有千秋.好吧,好记星不如烂笔头.当然,最重要的是,百度出来的都他妈的是如何添加环境变量,只 ...

  8. javascript动画系列第二篇——磁性吸附

    × 目录 [1]范围限定 [2]拖拽范围 [3]磁性吸附 前面的话 上一篇,我们介绍了元素拖拽的实现.但在实际应用中,常常需要为拖拽的元素限定范围.而通过限定范围,再增加一些辅助的措施,就可以实现磁性 ...

  9. C# Entity Framework并发处理

    原网站:C# Entity Framework并发处理 在软件开发过程中,并发控制是确保及时纠正由并发操作导致的错误的一种机制.从 ADO.NET 到 LINQ to SQL 再到如今的 ADO.NE ...

  10. cesium核心类Viewer简介

    1.简单描述Viewer Viewer类是cesium的核心类,是地图可视化展示的主窗口,cesium程序应用的切入口,扮演必不可少的核心角色. 官网的英文解析如下: A base widget fo ...