SDWebImage源代码阅读(二)
2016-09-13 23:06
11 查看
SDWebImage源代码阅读(二)
我们在SDWebImage源代码阅读(一)中讲了下SDWebImage的使用和UIImageView+WebCacheSDWebImageManager:
+ (id)sharedManager { static dispatch_once_t once; static id instance; dispatch_once(&once, ^{ instance = [self new]; }); return instance; }
SDWebImage是一个单例,OC中的单例就是这么简单
- (instancetype)init { SDImageCache *cache = [SDImageCache sharedImageCache]; SDWebImageDownloader *downloader = [SDWebImageDownloader sharedDownloader]; return [self initWithCache:cache downloader:downloader]; }
SDwebImageManager每次初始化都会有
SDImage和
SDWebImageDownloader的单例的赋值
- (id <SDWebImageOperation>)downloadImageWithURL:(NSURL *)url options:(SDWebImageOptions)options progress:(SDWebImageDownloaderProgressBlock)progressBlock completed:(SDWebImageCompletionWithFinishedBlock)completedBlock {}
这是SDWebImageManager最关键的方法,实现了,图片的加载保存等功能;progressBlock会在图片下载的时候去调用,completedBlock会在操作完成得时候调用
if ([url isKindOfClass:NSString.class]) { url = [NSURL URLWithString:(NSString *)url]; } if (![url isKindOfClass:NSURL.class]) { url = nil; }
首先,我们会对url进行一个判断,因为我们将NSString当成NSURL的这种错误的概率是非常高的,所以这里对是NSString类型的字符串进行了转换,保证之后代码的执行。当然,如果url既不是NSURL又不是NSString,这里就会对它进行nil的设置
__block SDWebImageCombinedOperation *operation =[SDWebImageCombinedOperation new]; __weak SDWebImageCombinedOperation *weakOperation = operation;
接下来这里初始化了一个新的
SDWebImageCombinedOperation类,并对其进行引用
BOOL isFailedUrl = NO; @synchronized (self.failedURLs) { isFailedUrl = [self.failedURLs containsObject:url]; }
这里
@synchronized的作用是创建一个互斥锁,保证没有其他线程对
self.failedURLs进行访问修改,起到保护作用。然后查询是否我们正在加载的这个图片的url是曾经加载失败过的url,并对
isFailedUrl赋值
if (url.absoluteString.length == 0 || (!(options & SDWebImageRetryFailed) && isFailedUrl)) { dispatch_main_sync_safe(^{ NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorFileDoesNotExist userInfo:nil]; completedBlock(nil, error, SDImageCacheTypeNone, YES, url); }); return operation; }
当url长度为0,或者是当self.failedURLs中包含了这个url(url代表的图片曾经加载失败),并且options设置不是SDWebImageRetryFailed(失败后重试),
dispatch_main_sync_safe主线程同步队列,初始化NSError并且给completedBlock赋值,并且返回
SDWebImageOperation变量
@synchronized (self.runningOperations) { [self.runningOperations addObject:operation]; }
如果url正常,或者错误重试,我们会在
self.runningOperations中添加这个操作
NSString *key = [self cacheKeyForURL:url];
根据url返回一个cacheKey,之后可以通过
SDWebImageCacheKeyFilterBlock去删除图像网址动态部分
operation.cacheOperation = [self.imageCache queryDiskCacheForKey:key done:^(UIImage *image, SDImageCacheType cacheType) { }
用
SDImageCahe的方法通过关键词去查询是否有
key的图片存在于高速缓存中或者磁盘中,并且给
SDWebImageCombinedOperation的属性
cacheOperation赋值。
if (operation.isCancelled) { @synchronized (self.runningOperations) { [self.runningOperations removeObject:operation]; } return;
当操作已经被取消
operation.isCancelled,我们将操作从
self.runningOperations中移除,并且退出该函数
if ((!image || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) { ··· }
当图片不存在或者要刷新图片缓存并且状态不属于阻止图片加载
if (image && options & SDWebImageRefreshCached) { dispatch_main_sync_safe(^{ // If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image // AND try to re-download it in order to let a chance to NSURLCache to refresh it from server. completedBlock(image, nil, cacheType, YES, url); }); }
当图片存在,而且需要刷新图片缓存的时候:赋值completedBlock
SDWebImageDownloaderOpti 4000 ons downloaderOptions = 0; if (options & SDWebImageLowPriority) downloaderOptions |= SDWebImageDownloaderLowPriority; if (options & SDWebImageProgressiveDownload) downloaderOptions |= SDWebImageDownloaderProgressiveDownload; if (options & SDWebImageRefreshCached) downloaderOptions |= SDWebImageDownloaderUseNSURLCache; if (options & SDWebImageContinueInBackground) downloaderOptions |= SDWebImageDownloaderContinueInBackground; if (options & SDWebImageHandleCookies) downloaderOptions |= SDWebImageDownloaderHandleCookies; if (options & SDWebImageAllowInvalidSSLCertificates) downloaderOptions |= SDWebImageDownloaderAllowInvalidSSLCertificates; if (options & SDWebImageHighPriority) downloaderOptions |= SDWebImageDownloaderHighPriority; if (image && options & SDWebImageRefreshCached) { // force progressive off if image already cached but forced refreshing downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload; // ignore image read from NSURLCache if image if cached but force refreshing downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse; }
根据options设置downloaderOptions(按位运算符)
id <SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished) { ··· }
通过url和options下载图片
__strong __typeof(weakOperation) strongOperation = weakOperation; if (!strongOperation || strongOperation.isCancelled)
__strong __typeof(weakOperation)因为之前是弱引用,我们需要保证在执行过程中weakOperation不变成nil,所以再加上一个强引用。
当strongOperation不为空或者说操作被取消了
我们什么也不做
当error是nil的时候
dispatch_main_sync_safe(^{ if (strongOperation && !strongOperation.isCancelled) { completedBlock(nil, error, SDImageCacheTypeNone, finished, url); } }); 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]; } }
主线程同步队列:如果strongOperation已经变为nil同时操作被取消了,我们给completedBlock赋值。同时error错误原因是我们列出来的,我们将会把url加入到
self.failedURLs中
当没有error,同时strongOperation为nil且操作没有被取消
if ((options & SDWebImageRetryFailed)) { @synchronized (self.failedURLs) { [self.failedURLs removeObject:url]; } }
当options是失败重试的时候,我们将对
self.failedURLs使用互斥锁,并且将url从
self.failedURLs移出
if (options & SDWebImageRefreshCached && image && !downloadedImage) { // Image refresh hit the NSURLCache cache, do not call the completion block }
如果options是刷新图片缓存同时image不是nil同时没有下载的图片,什么都不用执行,(图像刷新NSURLCache缓存,不调用完成闭包)
else if (downloadedImage && (!downloadedImage.images || (options & SDWebImageTransformAnimatedImage)) && [self.delegate respondsToSelector:@selector(imageManager:transformDownloadedImage:withURL:)]) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ 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); } }); }); }
当已经下载了图片了,且图片是动态图,启用了
SDWebImageTransformAnimatedImage,同时,委托有
imageManager:transformDownloadedImage:withURL:方法:高优先级异步执行图片转码,当转码完成后图片不为nil,通过self.imageCache保存图片缓存到磁盘上,并且同步执行comletedBlock的赋值
else { 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); } }); }
当图片不是动态图,同时不用刷新图片缓存的时候:如果下载图片完毕,调用
self.imageCache将图片缓存保存在磁盘上,同时同步执行comletedBlock的赋值
if (finished) { @synchronized (self.runningOperations) { if (strongOperation) { [self.runningOperations removeObject:strongOperation]; } } }
在图片完成保存,或者不需要保存之后,将operation从
self.runningOperations移出
operation.cancelBlock = ^{ [subOperation cancel]; @synchronized (self.runningOperations) { __strong __typeof(weakOperation) strongOperation = weakOperation; if (strongOperation) { [self.runningOperations removeObject:strongOperation]; } } };
设置操作取消的闭包,在操作取消的时候,我们会将操作从将operation从
self.runningOperations移出
当图片存在(同时不刷新图片缓存······就是上面那个条件不成立)
dispatch_main_sync_safe(^{ __strong __typeof(weakOperation) strongOperation = weakOperation; if (strongOperation && !strongOperation.isCancelled) { completedBlock(image, nil, cacheType, YES, url); } }); @synchronized (self.runningOperations) { [self.runningOperations removeObject:operation]; }
主线程同步队列:
__strong __typeof(weakOperation)因为之前是弱引用,我们需要保证在执行过程中weakOperation不变成nil,所以再加上一个强引用。如果strongOperation已经变为nil或者说操作被取消了,这时,我们需要给completeBlock赋值,最后从
self.runningOperations移除这个操作
当图片不存在,同时委托不允许加载图片
dispatch_main_sync_safe(^{ __strong __typeof(weakOperation) strongOperation = weakOperation; if (strongOperation && !weakOperation.isCancelled) { completedBlock(nil, nil, SDImageCacheTypeNone, YES, url); } }); @synchronized (self.runningOperations) { [self.runningOperations removeObject:operation];
主线程同步队列:
__strong __typeof(weakOperation)因为之前是弱引用,我们需要保证在执行过程中weakOperation不变成nil,所以再加上一个强引用。如果strongOperation已经变为nil或者说操作被取消了,这时,我们需要给completeBlock赋值,最后从
self.runningOperations移除这个操作,和上面不同的时completeBlock中cacheType是None
简而言之:SDWebImageManager
是一个单例管理类,负责协调图片缓存和图片下载,是对 SDImageCache和SDWebImageDownloader的封装,一般我们不用调用SDImageCache、SDWebImageDownloader,直接使用SDWebImageManager即可
@Blog
相关文章推荐
- SDWebImage源代码阅读(三)
- SDWebImage源代码阅读(四)
- SDWebImage源代码阅读(一)
- 向串口发送数据,并获得返回值 分类: java 2010-06-30 18:01 4244人阅读 评论(9) 收藏
- Oracle官方书籍阅读顺序
- 2014前端笔试阅读笔记
- GRE阅读中--高频词汇
- 第十三周阅读程序-交通工具类(2)
- Go语言阅读小笔记,来自知呼达达关于unsafe.Pointer的分享.
- Discuz!NT 代码阅读笔记(8)--DNT的几个分页存储过程解析
- 第十六周 阅读程序13.11 输出文件流
- 第6、7讲阅读程序结果检验6
- Uniform Generator 分类: HDU 2015-06-19 23:26 11人阅读 评论(0) 收藏
- 第12周阅读程序(1)
- 程序员阅读技术文章真的可以提升技术吗?
- 使用MFC.bsc文件阅读MFC源码方法
- 从阅读Java字节码来解决一些疑难杂症
- c和指针阅读笔记
- 树结构练习——排序二叉树的中序遍历 分类: 树 2015-06-21 11:05 12人阅读 评论(0) 收藏
- [PHP源码阅读]trim、rtrim、ltrim函数