美文网首页
SDWebImage 4.x版本源码分析(四)SDWebImag

SDWebImage 4.x版本源码分析(四)SDWebImag

作者: 快乐的老船长 | 来源:发表于2018-03-21 16:52 被阅读243次

    可以来这里下载一下源码注释

    5.SDWebImageCache

    问题:

    ①.SDImageCache是怎么存储内存缓存和磁盘缓存的?
    ②.NSCache 是什么?
    ③.磁盘缓存的路径是什么?
    ④.如何清理缓存?何时会自动清理缓存?
    ⑤.图片解压的作用是什么?
    ⑥.为什么要用NSMapTable?

    枚举

    typedef NS_ENUM(NSInteger, SDImageCacheType) {
    //没有缓存
        SDImageCacheTypeNone,
    //磁盘缓存
        SDImageCacheTypeDisk,
    //内存缓存
        SDImageCacheTypeMemory
    };
    
    typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) {
       //当图片缓存到内存中时,强制查询磁盘数据
        SDImageCacheQueryDataWhenInMemory = 1 << 0,
       //默认情况下,我们同步查询内存缓存,异步地查询磁盘缓存。此掩码可以同步查询磁盘缓存。
        SDImageCacheQueryDiskSync = 1 << 1
    };
    

    .h中的属性

    @property (nonatomic, nonnull, readonly) SDImageCacheConfig *config;
    
    //缓存应该持有的对象的最大数量。
    @property (assign, nonatomic) NSUInteger maxMemoryCountLimit;
    

    .m中的属性

    @property (strong, nonatomic, nonnull) SDMemoryCache *memCache;
    @property (strong, nonatomic, nonnull) NSString *diskCachePath;
    @property (strong, nonatomic, nullable) NSMutableArray<NSString *> *customPaths;
    @property (strong, nonatomic, nullable) dispatch_queue_t ioQueue;
    @property (strong, nonatomic, nonnull) NSFileManager *fileManager;
    

    .h中的方法

    //init
    + (nonnull instancetype)sharedImageCache;
    - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns;
    - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
                           diskCacheDirectory:(nonnull NSString *)directory NS_DESIGNATED_INITIALIZER;
    
    //缓存路径
    - (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace;
    - (void)addReadOnlyCachePath:(nonnull NSString *)path;
    
    //缓存操作
    - (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;
    
    //查询和检索操作
    - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock;
    - (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key;
    - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key done:(nullable SDCacheQueryCompletedBlock)doneBlock;
    - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock;
    - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key;
    - (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key;
    - (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key;
    
    //删除操作
    - (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
    - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion;
    
    //缓存清理
    - (void)clearMemory;
    - (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion;
    - (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock;
    
    //缓存配置
    - (NSUInteger)getSize;
    - (NSUInteger)getDiskCount;
    - (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock;
    
    //缓存路径
    - (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path;
    - (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key;
    

    .m中的方法

    //init dealloc
    + (nonnull instancetype)sharedImageCache
    - (instancetype)init 
    - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
    - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
                           diskCacheDirectory:(nonnull NSString *)directory
    - (void)dealloc
    
    //缓存路径
    - (void)addReadOnlyCachePath:(nonnull NSString *)path
    - (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path
    - (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key
    - (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key 
    - (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace
    
    //缓存操作
    - (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
    - (void)_storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key
    
    //查询和检索操作
    - (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock
    - (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key
    - (BOOL)_diskImageDataExistsWithKey:(nullable NSString *)key
    - (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key
    - (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key
    - (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key
    - (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key
    - (nullable UIImage *)diskImageForKey:(nullable NSString *)key
    - (nullable UIImage *)diskImageForKey:(nullable NSString *)key data:(nullable NSData *)data
    - (nullable UIImage *)scaledImageForKey:(nullable NSString *)key image:(nullable UIImage *)image
    - (NSOperation *)queryCacheOperationForKey:(NSString *)key done:(SDCacheQueryCompletedBlock)doneBlock
    - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock
    
    //删除操作
    - (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion
    - (void)removeImageForKey:(nullable NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(nullable SDWebImageNoParamsBlock)completion
    
    //内存缓存设置
    - (void)setMaxMemoryCost:(NSUInteger)maxMemoryCost
    - (NSUInteger)maxMemoryCost
    - (NSUInteger)maxMemoryCountLimit
    - (void)setMaxMemoryCountLimit:(NSUInteger)maxCountLimit
    
    //缓存清理
    - (void)clearMemory
    - (void)clearDiskOnCompletion:(nullable SDWebImageNoParamsBlock)completion
    - (void)deleteOldFiles
    - (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock
    
    //缓存配置
    - (NSUInteger)getSize
    - (NSUInteger)getDiskCount
    - (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock
    

    SDWebImageManager中直接调用的是 queryCacheOperationForKey这个方法,按照方法调用的顺序来看一下这里的实现:

    - (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock
    {
    //1.先检查内存缓存, 如果图片在内存中,并且没有强制要求查询磁盘,就返回
    //2.创建NSOperation对象
    //3.如果NSOperation对象取消了,就return,否则继续
    //4.开启异步队列,读取磁盘缓存,
    //5.如果有磁盘数据,就解码图像数据
    //6.如果解码成功,并且需要缓存到内存中,就添加到内存中
    //7.主线程中回调
    //8.return operation
    }
    

    关于查找磁盘缓存中的图片

    - (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key
    {
    //1. defaultCachePathForKey  读取磁盘缓存(沙盒)
    //2. 根据路径读取data,如果找到就返回data
    //3. 如果没有找到,去掉扩展名再试下
    //4. 如果还是没有,就读取bundle中的数据 (addReadOnlyCachePath 获取到的那个)
    }
    

    关于存储图片的路径

    - (nullable NSString *)cachePathForKey:(nullable NSString *)key inPath:(nonnull NSString *)path {
        // 经过 MD5 处理的文件名
        NSString *filename = [self cachedFileNameForKey:key];
        // path/<#MD5_filename#>
        return [path stringByAppendingPathComponent:filename];
    }
    - (nullable NSString *)defaultCachePathForKey:(nullable NSString *)key {
        //图片存储的路径为 Libray/Cache/<#namespace#>/com.hackemist.SDWebImageCache.<#namespace#>/<#MD5_filename#>
        return [self cachePathForKey:key inPath:self.diskCachePath];
    }
    - (nullable NSString *)cachedFileNameForKey:(nullable NSString *)key {
        const char *str = key.UTF8String;
        if (str == NULL) {
            str = "";
        }
        unsigned char r[CC_MD5_DIGEST_LENGTH];
        CC_MD5(str, (CC_LONG)strlen(str), r);
        NSURL *keyURL = [NSURL URLWithString:key];
        NSString *ext = keyURL ? keyURL.pathExtension : key.pathExtension;
        NSString *filename = [NSString stringWithFormat:@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@",
                              r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],
                              r[11], r[12], r[13], r[14], r[15], ext.length == 0 ? @"" : [NSString stringWithFormat:@".%@", ext]];
        return filename;
    }
    - (nullable NSString *)makeDiskCachePath:(nonnull NSString*)fullNamespace {
        // 获取cache目录路径
        NSArray<NSString *> *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
        return [paths[0] stringByAppendingPathComponent:fullNamespace];
    }
    

    关于自动清理磁盘缓存
    缓存的配置4.x版本放到了SDImageCacheConfig 里
    SDImageCacheConfig

    .h中的属性
    //解压缩图像下载和缓存可以提高性能,但是会消耗大量内存, 默认是YES ,如果因为消耗内存过大而崩溃,可以置为NO。
    //是否解压图片,默认YES
    @property (assign, nonatomic) BOOL shouldDecompressImages;
    //是否禁用 iCloud 备份,默认是 YES
    @property (assign, nonatomic) BOOL shouldDisableiCloud;
    //是否缓存到内存中,默认是YES
    @property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
    //读取磁盘缓存时的阅读选项。
    @property (assign, nonatomic) NSDataReadingOptions diskCacheReadingOptions;
    //在将缓存写入磁盘时写入选项。
    @property (assign, nonatomic) NSDataWritingOptions diskCacheWritingOptions;
    //在缓存中保存图像的最长时间,以秒为单位。(默认一周)
    @property (assign, nonatomic) NSInteger maxCacheAge;
    //缓存的最大大小,以字节为单位。
    @property (assign, nonatomic) NSUInteger maxCacheSize;
    

    .m

    #import "SDImageCacheConfig.h"
    
    static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 一周,默认超过7天的图片清除
    
    @implementation SDImageCacheConfig
    
    - (instancetype)init {
        if (self = [super init]) {
            _shouldDecompressImages = YES;
            _shouldDisableiCloud = YES;
            _shouldCacheImagesInMemory = YES;
            _diskCacheReadingOptions = 0;
            _diskCacheWritingOptions = NSDataWritingAtomic;
            _maxCacheAge = kDefaultCacheMaxCacheAge;
            _maxCacheSize = 0;
        }
        return self;
    }
    @end
    

    在SDImageCache里,内存缓存是放在 SDMemoryCache中,在SDMemoryCache中,当内存警告时,会删掉内存缓存
    SDMemoryCache继承于NSCache,所以在init方法里添加了观察者

    SDMemoryCache的 implementation

    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
    }
    - (instancetype)init {
        self = [super init];
        if (self) {
            // Use a strong-weak maptable storing the secondary cache. Follow the doc that NSCache does not copy keys
            // This is useful when the memory warning, the cache was purged. However, the image instance can be retained by other instance such as imageViews and alive.
            // At this case, we can sync weak cache back and do not need to load from disk cache
            self.weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
            self.weakCacheLock = dispatch_semaphore_create(1);
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(didReceiveMemoryWarning:)
                                                         name:UIApplicationDidReceiveMemoryWarningNotification
                                                       object:nil];
        }
        return self;
    }
    
    - (void)didReceiveMemoryWarning:(NSNotification *)notification {
        // Only remove cache, but keep weak cache
        [super removeAllObjects];
    }
    

    磁盘缓存的清理在SDImageCache中监听

    - (nonnull instancetype)initWithNamespace:(nonnull NSString *)ns
                           diskCacheDirectory:(nonnull NSString *)directory
    {
    ……
    
            //当程序将要终止时,异步删掉旧的文件
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(deleteOldFiles)
                                                         name:UIApplicationWillTerminateNotification
                                                       object:nil];
            //将要进去后台时,在后台异步删掉旧的文件
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     selector:@selector(backgroundDeleteOldFiles)
                                                         name:UIApplicationDidEnterBackgroundNotification
                                                       object:nil];
    
    ……
    
    }
    

    关于清理磁盘缓存的操作

    - (void)deleteOldFilesWithCompletionBlock:(nullable SDWebImageNoParamsBlock)completionBlock
    {
    1.创建异步操作
    2.获取缓存文件目录
    3. 计算过期日期
    4.清理图片
    4.1清理过期图片
    4.2清理超过最大缓存的图片
    }
    

    查询缓存流程图:

    image.png

    相关文章

      网友评论

          本文标题:SDWebImage 4.x版本源码分析(四)SDWebImag

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