SDWebImage源码解读之SDWebImageCache(上

作者: 老马的春天 | 来源:发表于2016-12-14 15:51 被阅读1029次

    第五篇

    前言

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

    我们在封装自己的图片缓存管理对象的时候,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,pathhttp://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(上

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