SDWebImage
的缓存策略
SDWebImage
的图片缓存默认情况采用的是Memory
和Disk
双重缓存机制。
下载之前先去Memory
中查找图片数据,找到直接返回使用;
找不到再到Disk
中查找图片数据,找到后放入Memory
中再返回使用;
如果Disk
中也找不到再去下载图片;
下载到图片后显示图片并将图片数据存到Memory
和Disk
中。
SDWebImage
的Memory
缓存的存取和删除机制
SDWebImage
的Memory
缓存使用NSCache
。SDImageCache
类中有一个memCache
属性。用这个属性来进行Memory
缓存。
@property (strong, nonatomic) NSCache *memCache;
下面代码用来将图片数据存到
Memory
中,其中的key
是图片的url
路径。
[self.memCache setObject:image forKey:key cost:cost];
下面代码用来获取
Memory
中的图片数据,其中的key
是图片的url
路径
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {
return [self.memCache objectForKey:key];
}
SDImageCache
类还监听了UIApplicationDidReceiveMemoryWarningNotification
通知,当收到内存警告时候清除Memory
缓存
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(clearMemory)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
- (void)clearMemory {
[self.memCache removeAllObjects];
}
SDWebImage
的Disk
缓存的存取和删除机制
SDWebImage
的Disk
缓存图片在/Library/Caches/default/com.hackemist.SDWebImageCache.default
文件夹下。且每个图片的存的文件名进行了处理:图片的文件名是图片url
路径MD5
后的字符串。存取都按照这个新的文件名去操作文件。
- (NSString *)cachedFileNameForKey:(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);
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], [[key pathExtension] isEqualToString:@""] ? @"" : [NSString stringWithFormat:@".%@", [key pathExtension]]];
return filename;
}
Disk缓存有时间限制,默认是一周。
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
SDImageCache
类监听了通知来删除Disk
中过期的图片缓存。
UIApplicationWillTerminateNotification
怎么实现
1.应用在前台,双击Home
键 ,终止应用 ,UIApplicationWillTerminateNotification
调用
2.应用在前台,单击Home
键,进入桌面 , 再终止应用UIApplicationWillTerminateNotification
不会被调用.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(cleanDisk)
name:UIApplicationWillTerminateNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(backgroundCleanDisk)
name:UIApplicationDidEnterBackgroundNotification
object:nil];
收到通知后,都会进入下面方法。在这个方法中将过期的
Disk
缓存清除。
- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock
SWebImage
防止一个无效的url
多次加载
SDWebImageManager
有个集合属性,这里包含着加载失败的url
。
@property (strong, nonatomic) NSMutableSet *failedURLs;
这个集合中保存着加载失败的
url
,下载之前先判断这个集合是否包含图片的下载url
。如果在且下载的策略不是SDWebImageRetryFailed
,那个直接返回下载错误,不再进行下载操作。
下载错误后判断,加入不是一些网络错误,就将这个图片url
添加进failedURLs
if (error.code != NSURLErrorNotConnectedToInternet
&& error.code != NSURLErrorCancelled
&& error.code != NSURLErrorTimedOut
&& error.code != NSURLErrorInternationalRoamingOff
&& error.code != NSURLErrorDataNotAllowed
&& error.code != NSURLErrorCannotFindHost
&& error.code != NSURLErrorCannotConnectToHost) {
@synchronized (self.failedURLs) {
[self.failedURLs addObject:url];
}
}
SDWebImage
防止同一个url
多次加载
前面已经说过,
SDWebImage
有缓存策略,再次下载是去缓存中取图片数据,这不就可以避免同一个url
多次下载了吗?
- 当然不可以了。缓存机制只是可以防止已经下载好的图片再次被下载
假如我同时(几乎同时)去下载同一个url的图片,这是肯定还没有缓存。
[imageView sd_setImageWithURL:[NSURL URLWithString:@"http://www.baidu.comimage/pic/item/1.jpg"]];
[imageView2 sd_setImageWithURL:[NSURL URLWithString:@"http://www.baidu.comimage/pic/item/1.jpg"]];
SDWebImage
又是如何处理的? SDWebImageDownloader
中有一个可变字典属性。
@property (strong, nonatomic) NSMutableDictionary *URLCallbacks;
通过下面的代码我们可以看出来字典中
key
是图片的url
,value
是一个可变数组,数组里面是一个一个的字典,每个字典中保存了下载过程回调和完成回调。每个字典相当于一次下载请求,但是只有第一次的下载请求才会真正去执行下载操作。这样就防止同一个url
多次加载。
/**
给下载过程添加进度
@param progressBlock 进度Block
@param completedBlock 完成Block
@param url url地址
@param createCallback nil
@return 返回SDWebImageDownloadToken。方便后面取消
*/
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
forURL:(nullable NSURL *)url
createCallback:(SDWebImageDownloaderOperation *(^)())createCallback {
// The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
if (url == nil) {
if (completedBlock != nil) {
completedBlock(nil, nil, nil, NO);
}
return nil;
}
__block SDWebImageDownloadToken *token = nil;
dispatch_barrier_sync(self.barrierQueue, ^{
//看是否当前url是否有对应的Operation图片加载对象
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
//如果没有,则直接创建一个。
if (!operation) {
//创建一个operation。并且添加到URLOperation中。
operation = createCallback();
self.URLOperations[url] = operation;
__weak SDWebImageDownloaderOperation *woperation = operation;
//设置operation操作完成以后的回调
operation.completionBlock = ^{
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
};
}
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
token = [SDWebImageDownloadToken new];
token.url = url;
token.downloadOperationCancelToken = downloadOperationCancelToken;
});
return token;
}
在图片下载完成或者取消下载,根据url
将URLCallbacks
中的数组移除。
http 304 Not Modified
SDWebImage
也对304这种情况做了相应了处理。 假如你读过SDWebImage
源码,该会对这个有点了解吧!
//This is the case when server returns '304 Not Modified'. It means that remote image is not changed.
//In case of 304 we need just cancel the operation and return cached image from the cache.
- 加入返回304直接取消下载操作并从缓存中返回图片数据。
网友评论