SDWebImage源码阅读-第二篇
一 SDWebImageManager的downloadImageWithURL的方法
上一篇,我们刚开了个头,分析了一下开始加载图片之前如何取消其他正在下载的任务,接着,我们回到
- (void)sd_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionBlock)completedBlock;
往下看。支持SDWebImageDelayPlaceholder,则优先显示placeholder。
if (!(options & SDWebImageDelayPlaceholder)) {
dispatch_main_async_safe(^{
self.image = placeholder;
});
}
然后是最关键的步骤,使用SDWebImageManager单例调用downloadImageWithURL方法请求image,并且返回operation,保存到operationDictionary中(这个上一篇有介绍,这里的operation不是真正加载图片的operation)。在completedBlock中取得下载的image,更新UI。
深入看看SDWebImageManager的
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionWithFinishedBlock)completedBlock
在这里研究了多久,主要是补了一下NSOperation的知识,需要的同学也可以去补一补:http://www.cocoachina.com/game/20151201/14517.html
我这里分析主要逻辑,其他一眼就能看明白的就不赘述了。这个方法里面做了几步优化:
- 维护一个URL黑名单failedURLs,下载失败后就加入黑名单,加入黑名单url再次加载的时候不会尝试去获取缓存或者重新下载,而是直接返回错误。
- 优先从缓存中获取,内存缓存,磁盘缓存都没有才去重新下载
这里要另提一句,由于涉及多线程,这里使用了@synchronized关键字实现了锁,保证线程安全。关于iOS如何实现锁请参考我的文章:oc中实现锁。
最主要的是第二步,我们深入了解一下SDImageCache的queryDiskCacheForKey方法。
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(SDWebImageQueryCompletedBlock)doneBlock {
if (!doneBlock) {
return nil;
} if (!key) {
doneBlock(nil, SDImageCacheTypeNone);
return nil;
} // First check the in-memory cache...
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 *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;
}
可以看到,第一步是从内存中获取缓存
UIImage *image = [self imageFromMemoryCacheForKey:key];
如果内存没有,再从磁盘获取,获取成功后再保存在内存中
UIImage *diskImage = [self diskImageForKey:key];
if (diskImage && self.shouldCacheImagesInMemory) {
NSUInteger cost = SDCacheCostForImage(diskImage);
[self.memCache setObject:diskImage forKey:key cost:cost];
}
再回到SDWebImageManager的downloadImageWithURL的方法,从缓存中没有获取到image,则要重新下载,下载的时候调用SDWebImageDownloader的downloadImageWithURL方法。下载成功后,对image进行处理。优先处理options中包含 SDWebImageTransformAnimatedImage的情况,意思是在image下载成功后,保存到缓存之前,对图片进行处理,处理交给delegate去做。处理完之后,把处理过的image保存下来。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, ), ^{
UIImage *transformedImage = [self.delegate imageManager:self transformDownloadedImage:downloadedImage withURL:url]; if (transformedImage && finished) {
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
[self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:(imageWasTransformed ? nil : data) forKey:key toDisk:cacheOnDisk];
} dispatch_main_sync_safe(^{
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(transformedImage, nil, SDImageCacheTypeNone, finished, url);
}
});
});
如果没有这种需求,就直接返回image并保存图片
if (downloadedImage && finished) {
[self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
} dispatch_main_sync_safe(^{
if (strongOperation && !strongOperation.isCancelled) {
completedBlock(downloadedImage, nil, SDImageCacheTypeNone, finished, url);
}
});
大家可能对dispatch_main_sync_safe这个宏很好奇,他其实就是保证在主线程,看看他的定义
#define dispatch_main_sync_safe(block)\
if ([NSThread isMainThread]) {\
block();\
} else {\
dispatch_sync(dispatch_get_main_queue(), block);\
}
接下来我们看看SDWebImage单纯的异步下载图片是怎么实现的,让我们来分析分析SDWebImageDownloader的downloadImageWithURL方法。
这里又涉及到多线程开发的另一种形式GCD,不熟悉的赶紧去补课。
该方法的第一步是调用addProgressCallback:completedBlock:forURL:createCallback方法,将progressBlock,completedBlock以URL为key保存到self.URLCallbacks中。并且,如果self.URLCallbacks中该URL下没有block,才会执行SDWebImageDownloaderOperation去下载图片。
- (void)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock forURL:(NSURL *)url createCallback:(SDWebImageNoParamsBlock)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;
} dispatch_barrier_sync(self.barrierQueue, ^{
BOOL first = NO;
if (!self.URLCallbacks[url]) {
self.URLCallbacks[url] = [NSMutableArray new];
first = YES;
} // Handle single download of simultaneous download request for the same URL
NSMutableArray *callbacksForURL = self.URLCallbacks[url];
NSMutableDictionary *callbacks = [NSMutableDictionary new];
if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
[callbacksForURL addObject:callbacks];
self.URLCallbacks[url] = callbacksForURL; if (first) {
createCallback();
}
});
}
这里有个GCD的方法大家可能比较陌生,dispatch_barrier_sync。它是同步插入一个任务,补课的同学移步这里:通过GCD中的dispatch_barrier_(a)sync加强对sync中所谓等待的理解。
真正执行下载任务的是createCallback。我们再来研究研究createCallback干了什么。
在这里,就开始真正发送网络请求,进行图片下载,超时时间为15s。创建一个SDWebImageDownloaderOperation对象,加入self.downloadQueue中异步执行。需要注意的是,下载完成,或者该operation被cancel,就会将那些block从self.URLCallbacks中移除,[self.URLCallbacks removeObjectForKey:url];回忆一下addProgressCallback:completedBlock:forURL:createCallback方法,这样保证正在下载的过程中不会再起线程去多次下载。
第二篇到此结束,主要的代码已经分析完毕,第三篇会讲一些不常用的方法。咱们下篇见!
SDWebImage源码阅读-第二篇的更多相关文章
- SDWebImage源码阅读-第一篇
一 题外话 之前写过一篇最新版SDWebImage的使用,也简单的介绍了一下原理.这两天正梳理自己的知识网络,觉得有必要再阅读一下源码,一是看具体实现,二是学习一下优秀开源代码的代码风格,比如接口设计 ...
- Flask源码阅读-第二篇(flask\__init__.py)
源码: # -*- coding: utf-8 -*-""" flask ~~~~~ A microframework based on Werkzeug. It's e ...
- SDWebImage源码阅读-第三篇
这一篇讲讲不常用的一些方法. 1 sd_setImageWithPreviousCachedImageWithURL: placeholderImage: options: progress: com ...
- 【原】SDWebImage源码阅读(二)
[原]SDWebImage源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 解决上一篇遗留的坑 上一篇中对sd_setImageWithURL函数简单分析了一下,还 ...
- 【原】SDWebImage源码阅读(四)
[原]SDWebImage源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 SDWebImage中主要实现了NSURLConnectionDataDelega ...
- 【原】SDWebImage源码阅读(三)
[原]SDWebImage源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1.SDWebImageDownloader中的downloadImageWithURL 我们 ...
- 【原】SDWebImage源码阅读(五)
[原]SDWebImage源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 前面的代码并没有特意去讲SDWebImage的缓存机制,主要是想单独开一章节专门讲 ...
- 【原】SDWebImage源码阅读(一)
[原]SDWebImage源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 一直没有系统地读过整套源码,就感觉像一直看零碎的知识点,没有系统读过一本专业经典书 ...
- SDWebImage 源码阅读分享
SDWebImage 源码阅读分享 疑问列表 SDWebImage 整体框架图,主要的类包含哪些 SDWebImage 如何进行缓存管理,过期失效策略,缓存更新 SDWebImage 如何多线程处理的 ...
随机推荐
- Linux DDos防御
今天要说的就是一款能够自动屏蔽DDOS攻击者IP的软件:DDoS deflate. DDoS deflate介绍 DDoS deflate是一款免费的用来防御和减轻DDoS攻击的脚本.它通过netst ...
- Python与rrdtool的结合模块
rrdtool(round robin database)工具为环状数据库的存储格式,round robin是一种处理定量数据以及当前元素指针的技术.rrdtool主要用来跟踪对象的变化情况,生成这些 ...
- PAT甲题题解-1117. Eddington Number(25)-(大么个大水题~)
如题,大水题...贴个代码完事,就这么任性~~ #include <iostream> #include <cstdio> #include <algorithm> ...
- 团队作业之旅游行业APP分析
随着经济的发展,不论是在工作中的男女老少,还是在校园中的童鞋,都喜欢在假期来一场说走就走的旅行,来缓解生活中的各种压力.当然,在国家面临经济转型的情况下,更多的将工业,农业转向服务型的旅游业,各个省市 ...
- bash基本功能 -命令的别名和快捷键
命令的别名 == 人的小名 如何查看和设定别名 alias 查看系统中的所有别名 ls --color=auto alias ll = 'ls - l --color=auto' touch abc ...
- 正则js
匹配中文字符的正则表达式: [\u4e00-\u9fa5] 匹配双字节字符(包括汉字在内):[^\x00-\xff] 匹配空行的正则表达式:\n[\s| ]*\r 匹配HTML标记的正则表达式:/&l ...
- [转帖]win10 .Net Runtime Optimization Service占用大量CPU资源解决方法
win10 .Net Runtime Optimization Service占用大量CPU资源解决方法 https://blog.csdn.net/cwg2552298/article/detail ...
- ESXi 系统管理员命令 [转帖]
来源 https://blog.csdn.net/zy_strive_2012/article/details/53336000 正如所有的虚拟化管理员都知道,要应对VMware基础设施上需要的更多虚 ...
- Java之序列化和反序列化
序列化的对象: package test_demo.SerializableOper; import java.io.Serializable; /* * 序列化对象需要实现序列号接口 * */ pu ...
- jQuery3 slim版本和普通版本区别,如何选择?
区别概述: slim即简化版,比普通版本缺少Ajax和特效模块模块. 官方发布地址:http://blog.jquery.com/2017/03/20/jquery-3-2-1-now-availab ...