sdwebimage不缓存图片的内存缓存是怎么实现的

推荐这篇日记的豆列
······存 取 删 路径
是在storeImage这个方法里:
将图片储存到内存和硬盘上
-(void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk {
if (!image || !key) {
// if memory cache is enabled
if (self.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(image);
[self.memCache setObject:image forKey:key cost:cost];
if (toDisk) {
dispatch_async(self.ioQueue, ^{
NSData *data = imageD
// 如果image存在,但是需要重新计算(recalculate)或者data为空
// 那就要根据image重新生成新的data
// 不过要是连image也为空的话,那就别存了
if (image && (recalculate || !data)) {
#if TARGET_OS_IPHONE
// 我们需要判断image是PNG还是JPEG
// PNG的图片很容易检测出来,因为它们有一个特定的标示 (http://www.w3.org/TR/PNG-Structure.html)
// PNG图片的前8个字节不许符合下面这些值(十进制表示)
// 137 80 78 71 13 10 26 10
// 如果imageData为空l (举个例子,比如image在下载后需要transform,那么就imageData就会为空)
// 并且image有一个alpha通道, 我们将该image看做PNG以避免透明度(alpha)的丢失(因为JPEG没有透明色)
int alphaInfo = CGImageGetAlphaInfo(image.CGImage);// 获取image中的透明信息
// 该image中确实有透明信息,就认为image为PNG
BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
alphaInfo == kCGImageAlphaNoneSkipFirst ||
alphaInfo == kCGImageAlphaNoneSkipLast);
BOOL imageIsPng = hasA
// 但是如果我们已经有了imageData,我们就可以直接根据data中前几个字节判断是不是PNG
if ([imageData length] &= [kPNGSignatureData length]) {
// ImageDataHasPNGPreffix就是为了判断imageData前8个字节是不是符合PNG标志
imageIsPng = ImageDataHasPNGPreffix(imageData);
// 如果image是PNG格式,就是用UIImagePNGRepresentation将其转化为NSData,否则按照JPEG格式转化,并且压缩质量为1,即无压缩
if (imageIsPng) {
data = UIImagePNGRepresentation(image);
data = UIImageJPEGRepresentation(image, (CGFloat)1.0);
// 当然,如果不是在iPhone平台上,就使用下面这个方法。不过不在我们研究范围之内
data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil];
// 获取到需要存储的data后,下面就要用fileManager进行存储了
if (data) {
// 首先判断disk cache的文件路径是否存在,不存在的话就创建一个
// disk cache的文件路径是存储在_diskCachePath中的
if (![_fileManager fileExistsAtPath:_diskCachePath]) {
[_fileManager createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:NULL];
// 根据image的key(一般情况下理解为image的url)组合成最终的文件路径
// 上面那个生成的文件路径只是一个文件目录,就跟/cache/images/img1.png和cache/images/的区别一样
NSString *cachePathForKey = [self defaultCachePathForKey:key];
// 这个url可不是网络端的url,而是file在系统路径下的url
// 比如/foo/bar/baz --------& file:///foo/bar/baz
NSURL *fileURL = [NSURL fileURLWithPath:cachePathForKey];
// 根据存储的路径(cachePathForKey)和存储的数据(data)将其存放到iOS的文件系统
[_fileManager createFileAtPath:cachePathForKey contents:data attributes:nil];
// disable iCloud backup
if (self.shouldDisableiCloud) {
[fileURL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
内存缓存使用NSCache的objectForKey取数据:
- (UIImage *)imageFromMemoryCacheForKey:(NSString *)key {
return [self.memCache objectForKey:key];
磁盘取数据 不断用 dataWithContentsOfFile来试数据是否在key对应的路径中
- (UIImage *)imageFromDiskCacheForKey:(NSString *)key {
// First check the in-memory cache...
UIImage *image = [self imageFromMemoryCacheForKey:key];
if (image) {
// Second check the disk cache...
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
return diskI
removeImageForKeyfromDisk:withCompletion: // 异步地将image从缓存(内存缓存以及可选的磁盘缓存)中移除
clearMemory // 清楚内存缓存上的所有image
clearDisk // 清除磁盘缓存上的所有image
cleanDisk // 清除磁盘缓存上过期的image
看其中最长的一个:
// 实现了一个简单的缓存清除策略:清除修改时间最早的file
- (void)cleanDiskWithCompletionBlock:(SDWebImageNoParamsBlock)completionBlock {
dispatch_async(self.ioQueue, ^{
// 这两个变量主要是为了下面生成NSDirectoryEnumerator准备的
// 一个是记录遍历的文件目录,一个是记录遍历需要预先获取文件的哪些属性
NSURL *diskCacheURL = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
NSArray *resourceKeys = @[NSURLIsDirectoryKey, NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];
// 递归地遍历diskCachePath这个文件夹中的所有目录,此处不是直接使用diskCachePath,而是使用其生成的NSURL
// 此处使用includingPropertiesForKeys:resourceKeys,这样每个file的resourceKeys对应的属性也会在遍历时预先获取到
// NSDirectoryEnumerationSkipsHiddenFiles表示不遍历隐藏文件
NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
includingPropertiesForKeys:resourceKeys
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];
// 获取文件的过期时间,SDWebImage中默认是一个星期
// 不过这里虽然称*expirationDate为过期时间,但是实质上并不是这样。
// 其实是这样的,比如在/00:00:00最后一次修改文件,对应的过期时间应该是
// /00:00:00,不过现在时间是/00:00:00,我先将当前时间减去1个星期,得到
// /00:00:00,这个时间才是我们函数中的expirationDate。
// 用这个expirationDate和最后一次修改时间modificationDate比较看谁更晚就行。
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-self.maxCacheAge];
// 用来存储对应文件的一些属性,比如文件所需磁盘空间
NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
// 记录当前已经使用的磁盘缓存大小
NSUInteger currentCacheSize = 0;
// 在缓存的目录开始遍历文件.
此次遍历有两个目的:
1. 移除过期的文件
2. 同时存储每个文件的属性(比如该file是否是文件夹、该file所需磁盘大小,修改时间)
NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];
for (NSURL *fileURL in fileEnumerator) {
NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourceKeys error:NULL];
// 当前扫描的是目录,就跳过
if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
// 移除过期文件
// 这里判断过期的方式:对比文件的最后一次修改日期和expirationDate谁更晚,如果expirationDate更晚,就认为该文件已经过期,具体解释见上面
NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
[urlsToDelete addObject:fileURL];
// 计算当前已经使用的cache大小,
// 并将对应file的属性存到cacheFiles中
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
[cacheFiles setObject:resourceValues forKey:fileURL];
for (NSURL *fileURL in urlsToDelete) {
// 根据需要移除文件的url来移除对应file
[_fileManager removeItemAtURL:fileURL error:nil];
// 如果我们当前cache的大小已经超过了允许配置的缓存大小,那就删除已经缓存的文件。
// 删除策略就是,首先删除修改时间更早的缓存文件
if (self.maxCacheSize & 0 && currentCacheSize & self.maxCacheSize) {
// 直接将当前cache大小降到允许最大的cache大小的一般
const NSUInteger desiredCacheSize = self.maxCacheSize / 2;
// 根据文件修改时间来给所有缓存文件排序,按照修改时间越早越在前的规则排序
NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
usingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
// 每次删除file后,就计算此时的cache的大小
// 如果此时的cache大小已经降到期望的大小了,就停止删除文件了
for (NSURL *fileURL in sortedFiles) {
if ([_fileManager removeItemAtURL:fileURL error:nil]) {
// 获取该文件对应的属性
NSDictionary *resourceValues = cacheFiles[fileURL];
// 根据resourceValues获取该文件所需磁盘空间大小
NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
// 计算当前cache大小
currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];
if (currentCacheSize & desiredCacheSize) {
// 如果有completionBlock,就在主线程中调用
if (completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
completionBlock();
1.4 图片储存路径
// 简单封装了cachePathForKey:inPath
- (NSString *)defaultCachePathForKey:(NSString *)key {
return [self cachePathForKey:key inPath:self.diskCachePath];
// cachePathForKey:inPath
- (NSString *)cachePathForKey:(NSString *)key inPath:(NSString *)path {
// 根据传入的key创建最终要存储时的文件名
NSString *filename = [self cachedFileNameForKey:key];
// 将存储的文件路径和文件名绑定在一起,作为最终的存储路径
return [path stringByAppendingPathComponent:filename];
// cachedFileNameForKey:
- (NSString *)cachedFileNameForKey:(NSString *)key {
const char *str = [key UTF8String];
if (str == NULL) {
// 使用了MD5进行加密处理
// 开辟一个16字节(128位:md5加密出来就是128bit)的空间
unsigned char r[CC_MD5_DIGEST_LENGTH];
// 官方封装好的加密方法
// 把str字符串转换成了32位的16进制数列(这个过程不可逆转) 存储到了r这个空间中
CC_MD5(str, (CC_LONG)strlen(str), r);
// 最终生成的文件名就是 &md5码&+&.文件类型&
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]]];
阅读(...) 评论()第一步,下载SDWebImage,导入工程。github托管地址
第二步,在需要的地方导入头文件
#import&"UIImageView+WebCache.h"
第三步,调用sd_setImageWithURL:方法缓存图片,注意,这就是新版本的新方法,旧方法是setImageWithURL:。下面将几个方法都介绍一下。
1. sd_setImageWithURL:
&&&&[self.image1&sd_setImageWithURL:imagePath1];
2. sd_setImageWithURL:& completed:
&&&&[self.image2&sd_setImageWithURL:imagePath2&completed:^(UIImage&*image,&NSError&*error,&SDImageCacheType&cacheType,&NSURL&*imageURL)&{
&&&&&&&&NSLog(@"这里可以在图片加载完成之后做些事情");
3. sd_setImageWithURL:& placeholderImage:
&&&&[self.image1&sd_setImageWithURL:imagePath1&placeholderImage:[UIImage&imageNamed:@"default"]];
4. sd_setImageWithURL:& placeholderImage:& completed:
&&&&[self.image1&sd_setImageWithURL:imagePath1&placeholderImage:[UIImage&imageNamed:@"default"]&completed:^(UIImage&*image,&NSError&*error,&SDImageCacheType&cacheType,&NSURL&*imageURL)&{
&&&&&&&&NSLog(@"图片加载完成后做的事情");
5. sd_setImageWithURL:& placeholderImage:& options:
&&&&[self.image1&sd_setImageWithURL:imagePath1&placeholderImage:[UIImage&imageNamed:@"default"]&options:SDWebImageRetryFailed];
其 他就不一一介绍了,oc是自文档语言,看方法名就知道干什么的了。除了带options选项的方法,其他的方法都是综合存储,也就是内存缓存和磁盘缓存结 合的方式,如果你只需要内存缓存,那么在options这里选择SDWebImageCacheMemoryOnly就可以了。
如果不想深入了解,到这里你已经可以用SDWebimage进行图片缓存了,接下来我要解释options的所有选项,以及SDWebImage内部执行流程。
一、options所有选项:
&&&&&SDWebImageRetryFailed&=&1&&&&0,
&&&&&SDWebImageLowPriority&=&1&&&&1,
&&&&&SDWebImageCacheMemoryOnly&=&1&&&&2,
&&&&&SDWebImageProgressiveDownload&=&1&&&&3,
&&&&&SDWebImageRefreshCached&=&1&&&&4,
&&&&&SDWebImageContinueInBackground&=&1&&&&5,
&&&&&SDWebImageHandleCookies&=&1&&&&6,
&&&&&SDWebImageHighPriority&=&1&&&&8,
&&&&&SDWebImageDelayPlaceholder&=&1&&&&9,
&&&&&SDWebImageTransformAnimatedImage&=&1&&&&10,
二、SDWebImage内部实现过程
入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage 显示,然后 SDWebImageManager 根据 URL 开始处理图片。
进 入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交给 SDImageCache 从缓存查找图片是否已经下载 queryDiskCacheForKey:delegate:userInfo:.
先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage: 到 UIImageView+WebCache 等前端展示图片。
如果内存缓存中没有,生成 NSInvocationOperation 添加到队列开始从硬盘查找图片是否已经缓存。
根据 URLKey 在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:。
如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate 回调 imageCache:didFindImage:forKey:userInfo:。进而回调展示图片。
如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo:。
共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。
图片下载由 NSURLConnection 来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败。
connection:didReceiveData: 中利用 ImageIO 做了按图片下载进度加载效果。
connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
图片解码处理在一个 NSOperationQueue 完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,imageDecoder:didFinishDecodingImage:userInfo: 回调给 SDWebImageDownloader。
imageDownloader:didFinishWithImage: 回调给 SDWebImageManager 告知图片下载完成。
通知所有的 downloadDelegates 下载完成,回调给需要的地方展示图片。
将图片保存到 SDImageCache 中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。
SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。
SDWebImagePrefetcher 可以预先下载图片,方便后续使用。
从上面流程可以看出,当你调用setImageWithURL:方法的时候,他会自动去给你干这么多事,当你需要在某一具体时刻做事情的时候,你可以覆盖这些方法。比如在下载某个图片的过程中要响应一个事件,就覆盖这个方法:
&&&&SDWebImageManager&*manager&=&[SDWebImageManager&sharedManager];
&&&&[manager&downloadImageWithURL:imagePath2&options:SDWebImageRetryFailed&progress:^(NSInteger&receivedSize,&NSInteger&expectedSize)&{
&&&&&&&&NSLog(@"显示当前进度");
&&&&}&completed:^(UIImage&*image,&NSError&*error,&SDImageCacheType&cacheType,&BOOL&finished,&NSURL&*imageURL)&{
&&&&&&&&NSLog(@"下载完成");
阅读(...) 评论()【无限互联】iOS框架研究之SDWebImage的原理以及使用流程 秦峰-五星文库
免费文档下载
【无限互联】iOS框架研究之SDWebImage的原理以及使用流程 秦峰
导读:【无限互联】IOS框架研究之SDWebImage的原理以及使用流程,方便使用,SDWebImage加载图片的流程:,方便后续使用,管理类的使用位置:,存储一个图像到缓存是使用方法storeImage:forKey:,要使用storeImage:forKey:toDisk:方法的第三个参数带一负值,【无限互联】IOS框架研究之SDWebImage的原理以及使用流程SDWebImage这个类库提供
【无限互联】IOS框架研究之SDWebImage的原理以及使用流程
SDWebImage
这个类库提供一个UIImageView类别以支持加载来自网络的远程图片。具有缓存管理、异步下载、同一个URL下载次数控制和优化等特征。
SDWebImage 支持异步的图片下载+缓存,提供了 UIImageView+WebCacha 的 category,方便使用。SDWebImage加载图片的流程:
1. 入口 setImageWithURL:placeholderImage:options: 会先把 placeholderImage显示,然后 SDWebImageManager 根据 URL 开始处理图片。
2. 进入 SDWebImageManager-downloadWithURL:delegate:options:userInfo:,交给 SDImageCache 从缓存查找图片是否已经下载
queryDiskCacheForKey:delegate:userInfo:.
3. 先从内存图片缓存查找是否有图片,如果内存中已经有图片缓存,
SDImageCacheDelegate回调 imageCache:didFindImage:forKey:userInfo: 到 SDWebImageManager。
4. SDWebImageManagerDelegate 回调 webImageManager:didFinishWithImage: 到 UIImageView+WebCache等前端展示图片。
5. 如果内存缓存中没有,生成 NSInvocationOperation添加到队列开始从硬盘查找图片是否已经缓存。
6. 根据 URLKey在硬盘缓存目录下尝试读取图片文件。这一步是在 NSOperation 进行的操作,所以回主线程进行结果回调 notifyDelegate:。
7. 如果上一操作从硬盘读取到了图片,将图片添加到内存缓存中(如果空闲内存过小,会先清空内存缓存)。SDImageCacheDelegate回调
imageCache:didFindImage:forKey:userInfo:。进而回调展示图片。
8. 如果从硬盘缓存目录读取不到图片,说明所有缓存都不存在该图片,需要下载图片,回调 imageCache:didNotFindImageForKey:userInfo:。
9. 共享或重新生成一个下载器 SDWebImageDownloader 开始下载图片。 10. 图片下载由 NSURLConnection来做,实现相关 delegate 来判断图片下载中、下载完成和下载失败。
11. connection:didReceiveData: 中利用 ImageIO做了按图片下载进度加载效果。
12. connectionDidFinishLoading: 数据下载完成后交给 SDWebImageDecoder 做图片解码处理。
13. 图片解码处理在一个 NSOperationQueue完成,不会拖慢主线程 UI。如果有需要对下载的图片进行二次处理,最好也在这里完成,效率会好很多。
14. 在主线程 notifyDelegateOnMainThreadWithInfo: 宣告解码完成,
imageDecoder:didFinishDecodingImage:userInfo: 回调给
SDWebImageDownloader。
15. imageDownloader:didFinishWithImage: 回调给 SDWebImageManager告知图片下载完成。
16. 通知所有的 downloadDelegates下载完成,回调给需要的地方展示图片。 17. 将图片保存到 SDImageCache中,内存缓存和硬盘缓存同时保存。写文件到硬盘也在以单独 NSInvocationOperation 完成,避免拖慢主线程。
18. SDImageCache 在初始化的时候会注册一些消息通知,在内存警告或退到后台的时候清理内存图片缓存,应用结束的时候清理过期图片。
19. SDWI 也提供了 UIButton+WebCache 和 MKAnnotationView+WebCache,方便使用。
20. SDWebImagePrefetcher 可以预先下载图片,方便后续使用。
管理类的使用位置:
这个库最常用到的,是UIImageView的一个Category:UIImageView
(WebCache)。
这里面最常用的一个方法,就是根据URL,加载网络的图片。它的实现如下:
这个方法最大的好处就是,可以不需要改变UI的类,直接添加网络下载功能。 独立的异步图像下载
可能会单独用到异步图片下载,则一定要用downloaderWithURL:delegate:来建立一个SDWebImageDownloader实例。
downloader = [SDWebImageDownloader downloaderWithURL:url delegate:self]; 这样SDWebImageDownloaderDelegate协议的方法
imageDownloader:didFinishWithImage:被调用时下载会立即开始并完成。
独立的异步图像缓存
SDImageCache类提供一个创建空缓存的实例,并用方法imageForKey:来寻找当前缓存。 UIImage *myCachedImage = [[SDImageCache sharedImageCache]
imageFromKey:myCacheKey];
存储一个图像到缓存是使用方法storeImage: forKey:
[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey]; 默认情况下,图像将被存储在内存缓存和磁盘缓存中。如果仅仅是想内存缓存中,要使用storeImage:forKey:toDisk:方法的第三个参数带一负值
SDWebImage库的作用:
通过对UIImageView的类别扩展来实现异步加载替换图片的工作。
主要用到的对象:
1、UIImageView (WebCache)类别,入口封装,实现读取图片完成后的回调
2、SDWebImageManager,对图片进行管理的中转站,记录那些图片正在读取。 向下层读取Cache(调用SDImageCache),或者向网络读取对象(调用
SDWebImageDownloader)。
实现SDImageCache和SDWebImageDownloader的回调。
3、SDImageCache,根据URL的MD5摘要对图片进行存储和读取(实现存在内存中或者存在硬盘上两种实现)
实现图片和内存清理工作。
4、SDWebImageDownloader,根据URL向网络读取数据(实现部分读取和全部读取后再通知回调两种方式)
SDImageCache是怎么做数据管理的?
SDImageCache分两个部分,一个是内存层面的,一个是硬盘层面的。
内存层面的相当是个缓存器,以Key-Value的形式存储图片。当内存不够的时候会清除所有缓存图片。
用搜索文件系统的方式做管理,文件替换方式是以时间为单位,剔除时间大于一周的图片文件。
当SDWebImageManager向SDImageCache要资源时,先搜索内存层面的数据,如果有直接返回,没有的话去访问磁盘,将图片从磁盘读取出来,然后做Decoder,将图片对象放到内存层面做备份,再返回调用层。
包含总结汇报、人文社科、考试资料、外语学习、资格考试、教学教材、旅游景点、行业论文、word文档、党团工作以及【无限互联】iOS框架研究之SDWebImage的原理以及使用流程 秦峰等内容。
相关内容搜索

我要回帖

更多关于 sdwebimage 缓存大小 的文章

 

随机推荐