1、介绍
SDImageCache
是 SDWebImage
处理图片缓存的类。图片的存储是针对内存和磁盘有一点区别:内存中直接存储图片,磁盘中存中 imagedata
在 SDWebImageManager
中使用的存储图片、判断图片是否已存储在磁盘/内存等方法均是调用该类中的方法。SDWebImage
设计时做了很好的分工,以至于去分析代码都带有一种享受感。
简单做个思考,如果自己实现一个缓存类,需要做哪些东西:
1、初始化、缓存地址。
3、查询、删除、存储方法(增删改查功能)
4、计算缓存大小
5、计算缓存数量…
然后在接下来的分析中看看跟自己的思路的偏差:
在 SDImageCache
的 .h
文件中引用了 SDWebImageCompat
和 SDImageCacheConfig
头文件。SDWebImageCompat
类在 SDWebImageManager
结尾已经分析过,该类只包含一个方法,用来实现图片缩放的操作。
SDImageCacheConfig
类则是管理缓存配置信息的,这里先单独拉出来看下
2、SDImageCacheConfig
类
SDImageCacheConfig
类是用于配置缓存信息的,继承自 NSObject
。
2.1、.h文件
缓存配置过期类型,枚举1
2
3
4
5
6typedef NS_ENUM(NSUInteger, SDImageCacheConfigExpireType) {
//访问图片时,它将更新此值 (访问日期)
SDImageCacheConfigExpireTypeAccessDate,
//图片从磁盘缓存中获取 (修改日期)
SDImageCacheConfigExpireTypeModificationDate
};
定义配置属性如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56/**
* 解压缩下载和缓存的图片可以提高性能,但会占用大量内存。
* 默认为YES。 如果由于过多的内存消耗而遇到崩溃,请将此项设置为NO。
*/
//是否解压图片,默认YES
@property (assign, nonatomic) BOOL shouldDecompressImages;
/**
* 是否禁用iCloud备份,默认YES
*/
@property (assign, nonatomic) BOOL shouldDisableiCloud;
/**
* 是否使用内存缓存,默认YES
* 禁用内存缓存时,也会禁用弱内存缓存。
*/
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
/**
* 控制图片的弱内存缓存的选项.启用时, SDImageCache 的内存缓存将使用弱映射表在存储到内存的同时存储图像,并同时删除.
* 但是当触发内存警告时,由于弱映射表没有强烈的图像实例引用,即使内存缓存本身被清除,UIImageViews或其他实时实例强烈保留的一些图像也可以再次恢复,以避免 稍后从磁盘缓存或网络重新查询。 这可能对这种情况有所帮助,例如,当app进入后台并清除内存时,会在重新输入前景后导致单元格闪烁。
* 默认为YES。 您可以动态更改此选项。
*/
//是否使用弱内存缓存,默认为YES
@property (assign, nonatomic) BOOL shouldUseWeakMemoryCache;
/**
* 从磁盘读取缓存时的读取选项
* 默认为 0. 可以设置为 `NSDataReadingMappedIfSafe` 以提高性能.
*/
//磁盘缓存读取选项,枚举
@property (assign, nonatomic) NSDataReadingOptions diskCacheReadingOptions;
/**
* 将缓存写入磁盘时的写入选项
* 默认为 NSDataWritingAtomic. 可以将其设置为 `NSDataWritingWithoutOverwriting` 以防止覆盖现有文件
*/
//磁盘缓存写入选项,枚举
@property (assign, nonatomic) NSDataWritingOptions diskCacheWritingOptions;
/**
* 在缓存中保留图片的最长时间,秒为单位
*/
@property (assign, nonatomic) NSInteger maxCacheAge;
/**
* 缓存的最大值,字节为单位,默认为0,表示不做限制
*/
@property (assign, nonatomic) NSUInteger maxCacheSize;
/**
* 清理磁盘缓存时将检查清理缓存的属性
* 默认修改日期
*/
//缓存配置过期类型,枚举 ,默认修改日期
@property (assign, nonatomic) SDImageCacheConfigExpireType diskCacheExpireType;
2.2、.m文件
静态不可变 NSInteger
类型的 kDefaultCacheMaxCacheAge
表示在缓存中图像保存时间的最大长度,以秒为单位 默认是一周时间(60 * 60 * 24 * 7)。_maxCacheAge
属性在 .h
中声明,可以外部修改。
1 | static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week |
3、SDImageCache
类
SDImageCache
维护内存缓存和可选的磁盘缓存。磁盘缓存写入操作是异步执行的,因此不会给UI增加不必要的延迟。
3.1、.h文件
缓存类型,枚举
1 | typedef NS_ENUM(NSInteger, SDImageCacheType) { |
缓存选项,枚举
1 | typedef NS_OPTIONS(NSUInteger, SDImageCacheOptions) { |
三个回调代码块
1 | //查询完成的block |
SDImageCache的属性
1 | /** |
SDImageCache的单例和初始化
1 | /** |
缓存路径
1 | //初始化磁盘缓存路径 |
存储操作
1 | /** |
查询和检索操作
1 | /** |
移除操作
1 | /** |
缓存清理操作
1 | /** |
缓存信息
1 | /** |
缓存路径
1 | /** |
3.2、.m文件
C语言函数,本质是计算diskImage所要占用的字节数。1
2
3
4
5
6
7FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
#if SD_MAC
return image.size.height * image.size.width;
#elif SD_UIKIT || SD_WATCH
return image.size.height * image.size.width * image.scale * image.scale;
#endif
}
私有1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123// Private
@interface SDMemoryCache <KeyType, ObjectType> ()
@property (nonatomic, strong, nonnull) SDImageCacheConfig *config;
@property (nonatomic, strong, nonnull) NSMapTable<KeyType, ObjectType> *weakCache; // strong-weak cache
@property (nonatomic, strong, nonnull) dispatch_semaphore_t weakCacheLock; // a lock to keep the access to `weakCache` thread-safe
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithConfig:(nonnull SDImageCacheConfig *)config;
@end
@implementation SDMemoryCache
目前这似乎没有用在macOS上(macOS使用虚拟内存,并且在内存警告时不清除缓存)。 所以我们只在iOS / tvOS平台上覆盖。
//但是将来可能会有更多的子类选项和功能。
#if SD_UIKIT
- (void)dealloc {
//移除内存警告通知
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
- (instancetype)initWithConfig:(SDImageCacheConfig *)config {
self = [super init];
if (self) {
// 使用存储二级缓存的强弱映射表。 按照NSCache不复制密钥的文档
// 当内存警告,缓存被清除时,这很有用。 但是,图像实例可以由其他实例保留,例如imageViews和alive。
// 在这种情况下,我们可以同步弱缓存,而不需要从磁盘缓存加载
self.weakCache = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsWeakMemory capacity:0];
self.weakCacheLock = dispatch_semaphore_create(1);
self.config = config;
//添加内粗警告的通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(didReceiveMemoryWarning:)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
return self;
}
- (void)didReceiveMemoryWarning:(NSNotification *)notification {
//只删除缓存,但保持弱缓存
[super removeAllObjects];
}
// `setObject:forKey:` 只需调用0即可,覆盖这就足够了
- (void)setObject:(id)obj forKey:(id)key cost:(NSUInteger)g {
//调用系统的NSCache方法
[super setObject:obj forKey:key cost:g];
//如果缓存配置不使用弱内存缓存,返回
if (!self.config.shouldUseWeakMemoryCache) {
return;
}
if (key && obj) {
//若果key和obj存在,存储弱缓存
LOCK(self.weakCacheLock);
[self.weakCache setObject:obj forKey:key];
UNLOCK(self.weakCacheLock);
}
}
//通过key获取object
- (id)objectForKey:(id)key {
id obj = [super objectForKey:key];
if (!self.config.shouldUseWeakMemoryCache) {
return obj;
}
if (key && !obj) {
// 若果key存在,obj不存在,存储弱缓存
LOCK(self.weakCacheLock);
obj = [self.weakCache objectForKey:key];
UNLOCK(self.weakCacheLock);
if (obj) {
//同步缓存
NSUInteger cost = 0;
if ([obj isKindOfClass:[UIImage class]]) {
//diskImage所要占用的字节数
cost = SDCacheCostForImage(obj);
}
[super setObject:obj forKey:key cost:cost];
}
}
return obj;
}
//根据key移除对象
- (void)removeObjectForKey:(id)key {
[super removeObjectForKey:key];
//如果缓存配置不使用弱内存缓存,返回
if (!self.config.shouldUseWeakMemoryCache) {
return;
}
if (key) {
// 如果key存在,移除缓存
LOCK(self.weakCacheLock);
[self.weakCache removeObjectForKey:key];
UNLOCK(self.weakCacheLock);
}
}
//移除所有对象
- (void)removeAllObjects {
[super removeAllObjects];
//如果缓存配置不使用弱内存缓存,返回
if (!self.config.shouldUseWeakMemoryCache) {
return;
}
// 手动删除也应该删除弱缓存
LOCK(self.weakCacheLock);
[self.weakCache removeAllObjects];
UNLOCK(self.weakCacheLock);
}
#else
//如果是macos,直接初始化
- (instancetype)initWithConfig:(SDImageCacheConfig *)config {
self = [super init];
return self;
}
#endif
@end
属性1
2
3
4
5
6
7
8
9
10
11
12
13
14@interface SDImageCache ()
#pragma mark - 属性
//内存缓存
@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;
@end
初始化
1 | #pragma mark - 单例, 初始化, dealloc |
缓存路径
1 | //添加只读缓存路径用来搜索由SDImageCache预先缓存的图片 |
存储操作
根据key将图片异步缓存到内存和磁盘中(默认存到内存和磁盘)1
2
3
4
5
6
7
8
9
10
11
12
13/**
* 根据key将图片异步缓存到内存和磁盘中
*
* @param image 需要缓存的图片
* @param key 唯一的缓存图片的key,通常是图像的绝对URL
* @param completionBlock 操作完成后执行的块
*/
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
[self storeImage:image imageData:nil forKey:key toDisk:YES completion:completionBlock];
}
根据key将图片异步缓存到内存和磁盘中 (默认存储到内存,根据判断是否存储到磁盘中)1
2
3
4
5
6
7
8
9
10
11
12
13
14/**
* 根据key将图片异步缓存到内存和磁盘中
* (根据toDisk来判断是否要存储到磁盘中,这里的磁盘缓存是可选的)
* @param image 需要缓存的图片
* @param key 唯一的缓存图片的key,通常是图像的绝对URL
* @param toDisk 是否缓存到磁盘中
* @param completionBlock 操作完成后执行的块
*/
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
[self storeImage:image imageData:nil forKey:key toDisk:toDisk completion:completionBlock];
}
根据key将图片异步缓存到内存和磁盘中 (默认存储到内存,根据判断是否存储到磁盘)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64/**
* 根据key将图片异步缓存到内存和磁盘中
* (这里面的方法会根据imageData如果没有,但是image有的话,就会考虑到图片格式的问题)
* @param image 需要缓存的图片
* @param imageData 服务器返回的图像数据,此表示将用于磁盘存储,而不是将给定的图像对象转换为可存储/压缩的图像格式,以节省质量和CPU
* @param key 唯一的缓存图片的key,通常是图像的绝对URL
* @param toDisk 是否缓存到磁盘中
* @param completionBlock 操作完成后执行的块 typedef void(^SDWebImageNoParamsBlock)(void); 不需要传任何参数
*/
- (void)storeImage:(nullable UIImage *)image
imageData:(nullable NSData *)imageData
forKey:(nullable NSString *)key
toDisk:(BOOL)toDisk
completion:(nullable SDWebImageNoParamsBlock)completionBlock {
//若图片或者key不存在,则不存储,执行回调,返回
if (!image || !key) {
if (completionBlock) {
completionBlock();
}
return;
}
// 如果启用了内存缓存
if (self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(image);
//根据key缓存image,
[self.memCache setObject:image forKey:key cost:cost];
}
//若需要缓存到磁盘
if (toDisk) {
//异步执行缓存操作
dispatch_async(self.ioQueue, ^{
@autoreleasepool { //自动释放池(里面创建了很多临时变量,当@autoreleasepool结束时,里面的内存就会回收)
NSData *data = imageData;
if (!data && image) {
// 如果我们没有任何数据来检测图像格式,请检查它是否包含使用PNG或JPEG格式的Alpha通道
SDImageFormat format;
if (SDCGImageRefContainsAlpha(image.CGImage)) {
format = SDImageFormatPNG;
} else {
format = SDImageFormatJPEG;
}
将图片编码为图片数据,该方法在SDWebImageCoder类中
data = [[SDWebImageCodersManager sharedInstance] encodedDataWithImage:image format:format];
}
//根据key存储imageData
[self _storeImageDataToDisk:data forKey:key];
}
//如果需要回调,在主线程执行回调
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
} else {
//如果不存储磁盘,执行完成回调
if (completionBlock) {
completionBlock();
}
}
}
根据key将图片data同步缓存到磁盘中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/**
* 根据key将图片data同步缓存到内存和磁盘中
*
* @param imageData 需要缓存的图片data
* @param key 唯一的缓存图片的key,通常是图像的绝对URL
*/
- (void)storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
//若图片或者key不存在,则不能存储
if (!imageData || !key) {
return;
}
dispatch_sync(self.ioQueue, ^{
[self _storeImageDataToDisk:imageData forKey:key];
});
}
根据key将图片data同步缓存到内存和磁盘中(确保通过调用者调用表单io队列)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24- (void)_storeImageDataToDisk:(nullable NSData *)imageData forKey:(nullable NSString *)key {
//若图片或者key不存在,则不能存储
if (!imageData || !key) {
return;
}
//如果文件管理器中不存在磁盘缓存的路径,则创建
if (![self.fileManager fileExistsAtPath:_diskCachePath]) {
[self.fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
}
//通过key获取缓存路径
NSString *cachePathForKey = [self defaultCachePathForKey:key];
// 转换成 NSUrl
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
//将图片写入fileURL中(options写入的选项,默认的配置为:NSDataWritingAtomic)
[imageData writeToURL:fileURL options:self.config.diskCacheWritingOptions error:nil];
// 禁用iCloud备份
if (self.config.shouldDisableiCloud) {
//NSURLIsExcludedFromBackupKey:如果应从备份中排除资源,则为true,否则为false(读写,值类型为boolean NSNumber)。 此属性仅用于排除备份中不需要的缓存和其他应用程序支持文件。 通常对用户文档执行的某些操作将导致此属性重置为false,因此不应在用户文档上使用此属性。
[fileURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}
查询和检索操作
异步检查磁盘缓存中是否存在图片(不加载图片),回调返回结果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/**
* 异步检查串行队列的磁盘缓存中是否存在图片(不加载图片),回调返回结果
*
* @param key 描述url的key
* @param completionBlock 检查完成时要执行的块。
* @note 将在主队列上始终执行完成块
*/
- (void)diskImageExistsWithKey:(nullable NSString *)key completion:(nullable SDWebImageCheckCacheCompletionBlock)completionBlock {
dispatch_async(self.ioQueue, ^{
// 根据key判断磁盘中是否图片数据
BOOL exists = [self _diskImageDataExistsWithKey:key];
if (completionBlock) {
//如果回调代码存在,主线程执行完成查询的回调
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock(exists);
});
}
});
}
同步检查磁盘缓存中是否存在图片(不加载图片),直接返回结果1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/**
* 同步检查磁盘缓存中是否存在图片(不加载图片),直接返回结果
*
* @param key 描述url的key
*/
- (BOOL)diskImageDataExistsWithKey:(nullable NSString *)key {
//如果key不存在,返回查询结果为NO ,否则同步根据key同步查询是否存在图片,返回结果
if (!key) {
return NO;
}
__block BOOL exists = NO;
dispatch_sync(self.ioQueue, ^{
exists = [self _diskImageDataExistsWithKey:key];
});
return exists;
}
根据key判断磁盘中是否存在图片数据1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16- (BOOL)_diskImageDataExistsWithKey:(nullable NSString *)key {
//如果key不存在,返回查询结果为NO
if (!key) {
return NO;
}
//key存在,则判断文件管理器中是否存在该key的缓存路径,如果存在返回,
BOOL exists = [self.fileManager fileExistsAtPath:[self defaultCachePathForKey:key]];
// 如果不存在,进一步判断是否存在该key删除扩展名的缓存路径,返回判断结果
if (!exists) {
//stringByDeletingPathExtension:从文件的最后一部分删除扩展名
exists = [self.fileManager fileExistsAtPath:[self defaultCachePathForKey:key].stringByDeletingPathExtension];
}
return exists;
}
根据key同步查询图片数据data1
2
3
4
5
6
7
8
9
10
11
12
13
14- (nullable NSData *)diskImageDataForKey:(nullable NSString *)key {
//如果key不存在,返回nil
if (!key) {
return nil;
}
//根据key搜索所有的路径获取磁盘图片data
__block NSData *imageData = nil;
dispatch_sync(self.ioQueue, ^{
imageData = [self diskImageDataBySearchingAllPathsForKey:key];
});
return imageData;
}
根据key同步查询内存缓存图片1
2
3- (nullable UIImage *)imageFromMemoryCacheForKey:(nullable NSString *)key {
return [self.memCache objectForKey:key];
}
根据key同步查询磁盘缓存图片1
2
3
4
5
6
7
8
9
10
11
12- (nullable UIImage *)imageFromDiskCacheForKey:(nullable NSString *)key {
//根据key查询磁盘缓存图片,最终调用的是:- (UIImage *)diskImageForKey: data: options:这个方法
UIImage *diskImage = [self diskImageForKey:key];
//如果图片存在,并且需要缓存到内存中,则计算所占用字节数,并缓存到内存中
if (diskImage && self.config.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
return diskImage;
}
检查缓存后,同步查询缓存(磁盘或内存)1
2
3
4
5
6
7
8
9
10
11- (nullable UIImage *)imageFromCacheForKey:(nullable NSString *)key {
// 先从内存中查询缓存图片,如果存在,结束查询并返回图片
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
return image;
}
//如果内存中未查到该key的图片,则从磁盘中查询,返回最后查询的结果
image = [self imageFromDiskCacheForKey:key];
return image;
}
根据key搜索所有的路径获取磁盘图片data1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32- (nullable NSData *)diskImageDataBySearchingAllPathsForKey:(nullable NSString *)key {
//根据key获取默认的缓存路径
NSString *defaultPath = [self defaultCachePathForKey:key];
//self.config.diskCacheReadingOptions :默认是0,即:NSDataReadingMappedIfSafe
//根据缓存路径和磁盘缓存读取选项,获取图片data,若存在则返回data,不存在则继续读取该key删除扩展名的缓存路径
NSData *data = [NSData dataWithContentsOfFile:defaultPath options:self.config.diskCacheReadingOptions error:nil];
if (data) {
return data;
}
//读取该key删除扩展名的缓存路径,获取图片data,若存在则返回data
data = [NSData dataWithContentsOfFile:defaultPath.stringByDeletingPathExtension options:self.config.diskCacheReadingOptions error:nil];
if (data) {
return data;
}
如果上面均未读取tupiandata,则依据上面的方法查找自定义路径,若存在返回data,如果仍未找到则返回nil
NSArray<NSString *> *customPaths = [self.customPaths copy];
for (NSString *path in customPaths) {
NSString *filePath = [self cachePathForKey:key inPath:path];
NSData *imageData = [NSData dataWithContentsOfFile:filePath options:self.config.diskCacheReadingOptions error:nil];
if (imageData) {
return imageData;
}
imageData = [NSData dataWithContentsOfFile:filePath.stringByDeletingPathExtension options:self.config.diskCacheReadingOptions error:nil];
if (imageData) {
return imageData;
}
}
return nil;
}
根据key获取磁盘图片1
2
3
4
5
6缓存图片到磁盘是存储的imageData;到内存是存储的image。
- (nullable UIImage *)diskImageForKey:(nullable NSString *)key {
//现获取imagedata,再转换成image
NSData *data = [self diskImageDataForKey:key];
return [self diskImageForKey:key data:data];
}
根据key和data得到磁盘图片1
2
3- (nullable UIImage *)diskImageForKey:(nullable NSString *)key data:(nullable NSData *)data {
return [self diskImageForKey:key data:data options:0];
}
//最终调用的方法,根据key和data、选项获取磁盘图片1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30- (nullable UIImage *)diskImageForKey:(nullable NSString *)key data:(nullable NSData *)data options:(SDImageCacheOptions)options {
//如果图片的data存在,进行进一步的转换,否则返回nil
if (data) {
//将data转换成image
UIImage *image = [[SDWebImageCodersManager sharedInstance] decodedImageWithData:data];
//对图片进行缩放操作
image = [self scaledImageForKey:key image:image];
//如果需要解压缩操作,就进行其操作,否则直接返回图片
if (self.config.shouldDecompressImages) {
//默认情况下,图像会根据其原始大小进行解码。在iOS上,此选项会将图像缩小到与设备的受限内存兼容的大小。
BOOL shouldScaleDown = options & SDImageCacheScaleDownLargeImages;
/**
*- (nullable UIImage *)decompressedImageWithImage:(nullable UIImage *)image
data:(NSData * _Nullable * _Nonnull)data
options:(nullable NSDictionary<NSString*, NSObject*>*)optionsDict;
* 使用原始图像和图像数据解压缩图像。
*
* @param image要解压缩的原始图像
* @param data指向原始图像数据的指针。 指针本身是非空的,但图像数据可以为空。 如果需要,此数据将设置为缓存。 如果您不需要同时修改数据,请忽略此参数。
* @param optionsDict包含任何解压缩选项的字典。 通过{SDWebImageCoderScaleDownLargeImagesKey:@(YES)}缩小大图像
* @return解压缩的图像
*/
image = [[SDWebImageCodersManager sharedInstance] decompressedImageWithImage:image data:&data options:@{SDWebImageCoderScaleDownLargeImagesKey: @(shouldScaleDown)}];
}
return image;
} else {
return nil;
}
}
//图片缩放操作1
2
3- (nullable UIImage *)scaledImageForKey:(nullable NSString *)key image:(nullable UIImage *)image {
return SDScaledImageForKey(key, image);
}
异步查询缓存并在完成后调用完成的操作1
2
3
4
5
6
7
8
9
10
11/**
* 异步查询缓存并在完成后调用完成的操作。
*
* @param key 用来存储所需图片唯一的key
* @param doneBlock The completion block. 如果操作被取消,则不会被调用
*
* @return 包含缓存操作的NSOperation实例
*/
- (NSOperation *)queryCacheOperationForKey:(NSString *)key done:(SDCacheQueryCompletedBlock)doneBlock {
return [self queryCacheOperationForKey:key options:0 done:doneBlock];
}
异步查询缓存并在完成后调用完成的操作1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78/**
* 异步查询缓存并在完成后调用完成的操作。
*
* @param key 用来存储所需图片唯一的key
* @param options 用于指定用于此高速缓存查询的选项
* @param doneBlock The completion block. 如果操作被取消,则不会被调用
*
* @return 包含缓存操作的NSOperation实例
*/
- (nullable NSOperation *)queryCacheOperationForKey:(nullable NSString *)key options:(SDImageCacheOptions)options done:(nullable SDCacheQueryCompletedBlock)doneBlock {
//如果key不存在,返回查询操作为nil,如果执行回调,则image,data传nil,类型传SDImageCacheTypeNone
if (!key) {
if (doneBlock) {
doneBlock(nil, nil, SDImageCacheTypeNone);
}
return nil;
}
// 先从内存中查找图片,
UIImage *image = [self imageFromMemoryCacheForKey:key];
BOOL shouldQueryMemoryOnly = (image && !(options & SDImageCacheQueryDataWhenInMemory));
如果image存在,并且只从内存中查找,返回NSOperation为nil,,如果执行回调,则传image为查找的image,data传nil,类型传SDImageCacheTypeMemory
if (shouldQueryMemoryOnly) {
if (doneBlock) {
doneBlock(image, nil, SDImageCacheTypeMemory);
}
return nil;
}
//创建一个NSOperation来获取磁盘图片
NSOperation *operation = [NSOperation new];
void(^queryDiskBlock)(void) = ^{
if (operation.isCancelled) {
// 如果操作被取消,则不执行回调
return;
}
//在自动释放池中执行,当@autoreleasepool结束时,里面的内存就会回收
@autoreleasepool {
//获取缓存data
NSData *diskData = [self diskImageDataBySearchingAllPathsForKey:key];
UIImage *diskImage;
SDImageCacheType cacheType = SDImageCacheTypeDisk;
if (image) {
// 如果从内存中查找的image存在,赋值给diskImage,缓存类型为SDImageCacheTypeMemory
diskImage = image;
cacheType = SDImageCacheTypeMemory;
} else if (diskData) {
// 如果内存缓存未找到image,并且缓存data存在,通过diskData转换为image
diskImage = [self diskImageForKey:key data:diskData options:options];
if (diskImage && self.config.shouldCacheImagesInMemory) {
//磁盘图片存在,并且需要缓存到内存,则做内存存储图片操作
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
}
//完成回调存在,如果选项为:SDImageCacheQueryDiskSync(此选项可以强制同步查询磁盘缓存),则执行同步回调,否则在主线程执行回调
if (doneBlock) {
if (options & SDImageCacheQueryDiskSync) {
doneBlock(diskImage, diskData, cacheType);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
doneBlock(diskImage, diskData, cacheType);
});
}
}
}
};
//如果选项为:SDImageCacheQueryDiskSync(此选项可以强制同步查询磁盘缓存),则执行同步执行上面的queryDiskBlock代码块,否则异步执行
if (options & SDImageCacheQueryDiskSync) {
queryDiskBlock();
} else {
dispatch_async(self.ioQueue, queryDiskBlock);
}
return operation;
}
移除操作
1 | /** |
缓存清理操作
1 | //清理缓存 |
缓存信息
获取磁盘缓存使用的大小
1 | - (NSUInteger)getSize { |
获取磁盘缓存中的图片数量
1 | - (NSUInteger)getDiskCount { |
异步计算磁盘缓存的大小
1 | - (void)calculateSizeWithCompletionBlock:(nullable SDWebImageCalculateSizeBlock)completionBlock { |
属性Get和Set方法
1 | //缓存中最大的消耗的内存,这里计算的是内存中的像素个数 |