SDWebImage源码(二)——SDImageCache缓存器

SDWebImage的缓存器

SDImageCacheSDWebImage的重要部件之一,它是一个单例类,完成了对图片的内存缓存、异步磁盘缓存、图片缓存查询等功能,这也是其优秀性能的原因所在,即下载过的图片将被缓存到内存和本地磁盘,当再次请求相同图片时直接从缓存中提取图片,从而大大提高了加载速度。SDWebImage的作者对核心方法都做了比较好的注释,这也大大提高了我们的阅读速度,我们先来看看头文件里的内容。

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
//缓存类型的枚举
typedef NS_ENUM(NSInteger, SDImageCacheType) {
//禁用缓存,直接从网络下载
SDImageCacheTypeNone,
//从磁盘获取缓存
SDImageCacheTypeDisk,
//从内存获取缓存
SDImageCacheTypeMemory
};
typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType cacheType);
typedef void(^SDWebImageCheckCacheCompletionBlock)(BOOL isInCache);
typedef void(^SDWebImageCalculateSizeBlock)(NSUInteger fileCount, NSUInteger totalSize);
/**
* SDImageCache maintains a memory cache and an optional disk cache. Disk cache write operations are performed
* asynchronous so it doesn’t add unnecessary latency to the UI.
*/
@interface SDImageCache : 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]
*/
//是否启用iCloud
@property (assign, nonatomic) BOOL shouldDisableiCloud;
/**
* use memory cache [defaults to YES]
*/
// 是否启用内存缓存
@property (assign, nonatomic) BOOL shouldCacheImagesInMemory;
/**
* The maximum "total cost" of the in-memory image cache. The cost function is the number of pixels held in memory.
*/
// 内存允许的最大内存容量,该数值是可以存储的最大像素数
@property (assign, nonatomic) NSUInteger maxMemoryCost;
/**
* The maximum number of objects the cache should hold.
*/
// 内存中允许的最大缓存数量
@property (assign, nonatomic) NSUInteger maxMemoryCountLimit;
/**
* 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;
/**
* Returns global shared cache instance
*
* @return SDImageCache global instance
*/
+ (SDImageCache *)sharedImageCache;
/**
* Init a new cache store with a specific namespace
*
* @param ns The namespace to use for this cache store
*/
//以ns 进行一些初始化操作
- (id)initWithNamespace:(NSString *)ns;
/**
* Init a new cache store with a specific namespace and directory
*
* @param ns The namespace to use for this cache store
* @param directory Directory to cache disk images in
*/
// 进行一些初始化操作
- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory;
//获取磁盘缓存路径
-(NSString *)makeDiskCachePath:(NSString*)fullNamespace;
/**
* 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:(NSString *)path;
/**
* 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
*/
// 以图片的请求路径作为唯一key保存图片到内存和磁盘缓存中
- (void)storeImage:(UIImage *)image forKey:(NSString *)key;
/**
* Store an image into memory and optionally 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
*/
//以图片的请求路径作为唯一key保存图片到内存和磁盘(可选)缓存中
- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk;
/**
* Store an image into memory and optionally disk cache at the given key.
*
* @param image The image to store
* @param recalculate BOOL indicates if imageData can be used or a new data should be constructed from the UIImage
* @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
*/
// 直接将图片的NSData(不转为图片对象)存储到内存或者磁盘中,节约空间和CPU占用
- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk;
/**
* Store image NSData into disk cache at the given key.
*
* @param imageData The image data to store
* @param key The unique image cache key, usually it's image absolute URL
*/
// 存储图片的二进制数据到磁盘缓存中
- (void)storeImageDataToDisk:(NSData *)imageData forKey:(NSString *)key;
/**
* Query the disk cache asynchronously.
*
* @param key The unique key used to store the wanted image
*/
//以key值异步查询磁盘中是否有该图片
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock;
/**
* Query the memory cache synchronously.
*
* @param key The unique key used to store the wanted image
*/
//以key值同步查询内存中是否有该图片
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key;
/**
* Query the disk cache synchronously after checking the memory cache.
*
* @param key The unique key used to store the wanted image
*/
// 同步查询磁盘中是否有某张图片
- (UIImage *)imageFromDiskCacheForKey:(NSString *)key;
/**
* Remove the image from memory and disk cache synchronously
*
* @param key The unique image cache key
*/
// 同步移除内存和磁盘中某张图片的缓存
- (void)removeImageForKey:(NSString *)key;
/**
* Remove the image from memory and disk cache asynchronously
*
* @param key The unique image cache key
* @param completion An block that should be executed after the image has been removed (optional)
*/
// 异步移除内存和磁盘中的某张图片的缓存,并执行完成回调
- (void)removeImageForKey:(NSString *)key withCompletion:(SDWebImageNoParamsBlock)completion;
/**
* 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
*/
// 异步移除内存和磁盘(可选)中的某张图片的缓存
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk;
/**
* 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 An block that should be executed after the image has been removed (optional)
*/
// 异步移除内存和磁盘(可选)中的某张图片的缓存,并执行完成回调
- (void)removeImageForKey:(NSString *)key fromDisk:(BOOL)fromDisk withCompletion:(SDWebImageNoParamsBlock)completion;
/**
* Clear all memory cached images
*/
// 清除所有内存缓存
- (void)clearMemory;
/**
* Clear all disk cached images. Non-blocking method - returns immediately.
* @param completion An block that should be executed after cache expiration completes (optional)
*/
// 清除磁盘缓存
- (void)clearDiskOnCompletion:(SDWebImageNoParamsBlock)completion;
- (void)clearDisk;
/**
* Remove all expired cached image from disk. Non-blocking method - returns immediately.
* @param completionBlock An block that should be executed after cache expiration completes (optional)
*/
// 移除磁盘中过期的缓存
- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock;
- (void)cleanDisk;
/**
* Get the size used by the disk cache
*/
// 获取磁盘缓存占用容量
- (NSUInteger)getSize;
/**
* Get the number of images in the disk cache
*/
// 获取磁盘缓存图片数量
- (NSUInteger)getDiskCount;
/**
* Asynchronously calculate the disk cache's size.
*/
// 异步计算磁盘缓存大小
- (void)calculateSizeWithCompletionBlock:(SDWebImageCalculateSizeBlock)completionBlock;
/**
* 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:(NSString *)key completion:(SDWebImageCheckCacheCompletionBlock)completionBlock;
- (BOOL)diskImageExistsWithKey:(NSString *)key;
/**
* 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
*/
// 以key值获取指定路径下的图片的磁盘缓存路径
- (NSString *)cachePathForKey:(NSString *)key inPath:(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
*/
// 以key值获取默认路径下的图片缓存路径
- (NSString *)defaultCachePathForKey:(NSString *)key;

虽然方法看上去挺多的但其实核心的方法就是对图片缓存的增、删、查的实现,只不过每种功能可能有多种实现方式,但本质上是一样的,我们只需关注核心的几个方法即可。

SDWebImage实现了内存缓存和磁盘缓存,内存缓存是通过NSCache实现,磁盘缓存是通过NSFileManager来实现文件的存储,磁盘缓存是异步实现的。

初始化

首先定义了一个继承于NSCache的类AutoPurgeCache,当收到内存警告时,默认清除所有缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@interface AutoPurgeCache : NSCache
@end
@implementation AutoPurgeCache
- (id)init
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}
return self;
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}

初始化SDImageCache

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
+ (SDImageCache *)sharedImageCache {
//创建单例类,copy会调用init方法
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
}
- (id)init {
return [self initWithNamespace:@"default"];
}
- (id)initWithNamespace:(NSString *)ns {
//获取磁盘缓存的默认路径
NSString *path = [self makeDiskCachePath:ns];
return [self initWithNamespace:ns diskCacheDirectory:path];
}
- (id)initWithNamespace:(NSString *)ns diskCacheDirectory:(NSString *)directory {
if ((self = [super init])) {
//缓存空间的默认名称
NSString *fullNamespace = [@"com.hackemist.SDWebImageCache." stringByAppendingString:ns];
// initialise PNG signature data
//用于验证png格式
kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8];
// Create IO serial queue
//初始化串行队列,用于异步写入磁盘
_ioQueue = dispatch_queue_create("com.hackemist.SDWebImageCache", DISPATCH_QUEUE_SERIAL);
// Init default values
//磁盘缓存存储时间,默认为一周
_maxCacheAge = kDefaultCacheMaxCacheAge;
// Init the memory cache
//初始化内存缓存
_memCache = [[AutoPurgeCache alloc] init];
_memCache.name = fullNamespace;
// Init the disk cache
//磁盘缓存的默认路径
if (directory != nil) {
_diskCachePath = [directory stringByAppendingPathComponent:fullNamespace];
} else {
NSString *path = [self makeDiskCachePath:ns];
_diskCachePath = path;
}
//下面是一些属性的初始化值
_shouldDecompressImages = YES;
_shouldCacheImagesInMemory = YES;
_shouldDisableiCloud = YES;
dispatch_sync(_ioQueue, ^{
//初始化文件管理器
_fileManager = [NSFileManager new];
});
//下面是一些清除缓存的通知
#if TARGET_OS_IOS
// Subscribe to app events
//收到内存警告时清除内存缓存
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(clearMemory)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
//每次进程杀死前都清理过期(默认大于一周)的磁盘缓存
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(cleanDisk)
name:UIApplicationWillTerminateNotification
object:nil];
//后台挂起时清理过期磁盘缓存
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundCleanDisk)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
#endif
}
return self;
}

以上就是SDImageCache的初始化过程,主要是初始化内存缓存对象以及初始化磁盘缓存目录,以及对一些默认参数的赋值。

缓存图片

虽然SDImageCache有多个存储图片的方法,但都是基于下面这一个方法并传入不同的参数而已:

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
- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk {
if (!image || !key) {
return;
}
// if memory cache is enabled
//是否允许内存缓存
if (self.shouldCacheImagesInMemory) {
//计算图片所占空间
//NSUInteger SDCacheCostForImage(UIImage *image) {
// return image.size.height * image.size.width * image.scale * image.scale;}
NSUInteger cost = SDCacheCostForImage(image);
//写入内存缓存
[self.memCache setObject:image forKey:key cost:cost];
}
if (toDisk) {
dispatch_async(self.ioQueue, ^{
NSData *data = imageData;
if (image && (recalculate || !data)) {
//如果需要重新计算(recalculate)或者data为0,需要将image转化为二进制数据
#if TARGET_OS_IPHONE
// We need to determine if the image is a PNG or a JPEG
// PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html)
// The first eight bytes of a PNG file always contain the following (decimal) values:
// 137 80 78 71 13 10 26 10
// If the imageData is nil (i.e. if trying to save a UIImage directly or the image was transformed on download)
// and the image has an alpha channel, we will consider it PNG to avoid losing the transparency
//这里这段代码做的一个工作是判断图片格式
//有两种方式:一种方式是判断二进制数据前8位是否是png特定格式(137 80 78 71 13 10 26 10);一种是判断图片是否有透明度
int alphaInfo = CGImageGetAlphaInfo(image.CGImage);
BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
alphaInfo == kCGImageAlphaNoneSkipFirst ||
alphaInfo == kCGImageAlphaNoneSkipLast);
//如果有透明度 则证明是png格式
BOOL imageIsPng = hasAlpha;
// But if we have an image data, we will look at the preffix
//如果有data数据,直接判断起始数据是否符合格式
if ([imageData length] >= [kPNGSignatureData length]) {
imageIsPng = ImageDataHasPNGPreffix(imageData);
}
//如果是png格式,转为data数据, 否则转为png
if (imageIsPng) {
data = UIImagePNGRepresentation(image);
}
else {
data = UIImageJPEGRepresentation(image, (CGFloat)1.0);
}
#else
//非iOS平台的处理,看不懂,总之就是获取图片的data数据
data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil];
#endif
}
//把二进制数据存入磁盘
[self storeImageDataToDisk:data forKey:key];
});
}
}

以上这段代码比较简单,主要是对传入参数的合法性做一些判断以及对image的格式做判断并获取NSData数据,继续看storeImageDataToDisk:方法:

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
- (void)storeImageDataToDisk:(NSData *)imageData forKey:(NSString *)key {
if (!imageData) {
return;
}
if (![_fileManager fileExistsAtPath:_diskCachePath]) {
//如果默认磁盘缓存路径不存在,则创建缓存文件夹
[_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
}
// get cache Path for image key
//得到单独文件的路径,这里对URL做了一个MD5加密+扩展名作为文件名,具体代码不贴了,源码里都可以看到
NSString *cachePathForKey = [self defaultCachePathForKey:key];
// transform to NSUrl
// 生成路径URL用以iCloud备份
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
//写入缓存文件
[_fileManager createFileAtPath:cachePathForKey contents:imageData attributes:nil];
// disable iCloud backup
if (self.shouldDisableiCloud) {
//iCloud备份,没有深入了解
[fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
}
}

至此,图片缓存的过程就完成了,这时候在沙盒Library/Cache/目录下就会有一份图片的缓存。
下面看一下查询图片的接口,查询内存缓存的接口比较简单,只需要一句话

1
2
3
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {
return [self.memCache objectForKey: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
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
// block回调传入image对象还有缓存对象
// typedef void(^SDWebImageQueryCompletedBlock)(UIImage *image, SDImageCacheType cacheType);
if (!doneBlock) {
return nil;
}
if (!key) {
doneBlock(nil, SDImageCacheTypeNone);
return nil;
}
// First check the in-memory cache...
//如果内存缓存中查询到了,执行成功block
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
doneBlock(image, SDImageCacheTypeMemory);
return nil;
}
//内存中没有,异步查询磁盘
NSOperation *operation = [NSOperation new];
dispatch_async(self.ioQueue, ^{
if (operation.isCancelled) {
return;
}
@autoreleasepool {
//对图片做了一系列的解码转换等操作,得到UIImage对象
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.shouldCacheImagesInMemory) {
//如果允许内存缓存,写入内存以提高下次查询效率
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
dispatch_async(dispatch_get_main_queue(), ^{
//回到主线程,执行成功回调
doneBlock(diskImage, SDImageCacheTypeDisk);
});
}
});
return operation;
}

磁盘查询也是非常简单的,至于这个接口为什么返回一个NSOperation对象,我们在单独讲SDWebImageManager时再说。
再来看一下清理缓存的接口,分两种情况, 一种是clear一种是cleanclear比较暴力,直接移除内存缓存的对象或者直接移除磁盘缓存文件夹。着重看一下cleanDisk

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
- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock {
//异步执行
dispatch_async(self.ioQueue, ^{
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
//需要获取的文件属性:是否是文件夹、修改时间、文件大小
NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
// This enumerator prefetches useful properties for our cache files.
// 枚举器,遍历磁盘缓存目录
NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
//过期日期:从现在往前推 maxCacheAge,超过这个日期的删除
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
NSUInteger currentCacheSize = 0;
// Enumerate all of the files in the cache directory. This loop has two purposes:
//
// 1. Removing files that are older than the expiration date.
// 2. Storing file attributes for the size-based cleanup pass.
NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];
for (NSURL *fileURL in fileEnumerator) {
NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];
// Skip directories.
//如果是文件夹,跳过
if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
continue;
}
// Remove files that are older than the expiration date;
//如果修改时间早于一周前,移除
NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
continue;
}
// Store a reference to this file and account for its total size.
//统计缓存总大小并暂存文件属性
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
[cacheFiles setObject:resourceValues forKey:fileURL];
}
for (NSURL *fileURL in urlsToDelete) {
//移除缓存文件
[_fileManager removeItemAtURL:fileURL error:nil];
}
// If our remaining disk cache exceeds a configured maximum size, perform a second
// size-based cleanup pass. We delete the oldest files first.
//如果剩下的文件总大小大于我们定义的总的内存大小,再循环一遍,删除相对较老的文件
if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) {
// Target half of our maximum cache size for this cleanup pass.
//设定一个期望大小——删到最大内存的一半
const NSUInteger desiredCacheSize = self.maxCacheSize / 2;
// Sort the remaining cache files by their last modification time (oldest first).
//按照文件修改时间由老到新排序
NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
}];
// Delete files until we fall below our desired cache size.
//对排序后的数组循环删除,知道缓存占用内存降到我们期望的数值,跳出循环
for (NSURL *fileURL in sortedFiles) {
if ([_fileManager removeItemAtURL:fileURL error:nil]) {
NSDictionary *resourceValues = cacheFiles[fileURL];
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];
if (currentCacheSize < desiredCacheSize) {
break;
}
}
}
}
if (completionBlock) {
//在主线程执行完成回调
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
});
}
});
}

以上就是SDImageCache的所有内容。

|