[image]20 发现大姨妈会传染吗染在了板凳上,过了一晚上,大姨妈会传染吗能直接用白水擦干净吗?板凳如下

“板凳教师”走红网络 教数学用板凳画图(图)
来源:广州日报
还记得上中学时,数学老师在黑板上画几何图的情景吗?近日,一位&板凳老师&走红网络,因为大家画图都用尺,而他用的却是&板凳&:拎起一条板凳,翻转过来扣在黑板上,沿着凳面画图。
&我们的教育经费都花到哪里去了?&许多网友的第一反应是质疑。记者了解到,该帖子原帖发布于百度山阳中学的贴吧上,图片上的这一幕就发生在陕西商洛山阳中学。
而事情发生的原因是,&那天数学老师画图像没拿尺子。用我们的小尺子觉得太小了,之后就垫着长板凳当尺子了。&
编辑:章明
安徽资讯APP
扫一扫,安徽尽在您手中
万家热线今日合肥
微信扫一扫,使用小程序
每天10分钟,通晓合肥事
1284人关注
1032人关注
《致匠心》紫苹果工艺巡检品鉴会,5.19 紫苹果“匠心 ...
期末冲刺季, 一对一家教服务免费申领啦!
新中国成立以后,中央人民政府政务院于1949年12月将5月1日定为法定的劳动节...
今日万家热线
扫描关注今日万家热线微信公共号
短信快捷登录
合作账号登录
验证即登录,未注册将自动创建万家账号
发送验证码搬好小板凳看SDWebImage源码解析(一)
招聘信息:
本文是来自@夏大的投稿看了下距离上次写简书博客的时间,已经过去了八个多月了,很惭愧。正好最近项目不忙,抽点时间研究下第三方库,朋友建议总结写成博客就这样开篇了。内容篇幅会比较长,所以希望各位看官搬好小板凳看SDWebImage源码解析,如果没有毅力真的是很难坚持下去。希望大家可以坚持跟着博主一块学完SDWebImage源码系列。一.准备知识在正式学习源码前,先讲一些SDWebImage中用到的生僻知识点,有些用的很频繁,但是很多人对这些知识点模糊不清,如果不搞清楚会大大影响阅读效率,比如枚举NS_OPTIONS的二进制位运算。1> NS_OPTIONS与位运算NS_OPTIONS用来定义位移相关操作的枚举值,当一个枚举变量需要携带多种值的时候就需要,我们可以参考UIKit.Framework的头文件,可以看到大量的枚举定义。例如在SDWebImage下面就会接触到SDWebImageOptions枚举值:typedef&NS_OPTIONS(NSUInteger,&SDWebImageOptions)&{
&&&&SDWebImageRetryFailed&=&1&<<&0,
&&&&SDWebImageLowPriority&=&1&<<&1,
&&&&SDWebImageCacheMemoryOnly&=&1&<<&2,
&&&&SDWebImageProgressiveDownload&=&1&<<&3,
&&&&SDWebImageRefreshCached&=&1&<<&4,
&&&&SDWebImageContinueInBackground&=&1&<<&5,
&&&&SDWebImageHandleCookies&=&1&<<&6,
&&&&SDWebImageAllowInvalidSSLCertificates&=&1&<<&7,
&&&&SDWebImageHighPriority&=&1&<<&8,
&&&&SDWebImageDelayPlaceholder&=&1&<<&9,
&&&&SDWebImageTransformAnimatedImage&=&1&<<&10,
&&&&SDWebImageAvoidAutoSetImage&=&1&<<&11,
&&&&SDWebImageScaleDownLargeImages&=&1&<<&12
};“<<”是位运算中的左移运算符,第一个值SDWebImageRetryFailed = 1 << 0,十进制1转化为二进制:0b,这里<<0将所有二进制位左移0位,那么还是0b,最终SDWebImageRetryFailed 值为1.第二个枚举值SDWebImageLowPriority =1<<1,这里是将1的二进制所有位向左移动1位,空缺的用0补齐,那么0b变成0b,十进制为2则SDWebImageLowPriority值为2。左移1位示意图依次类推:SDWebImageCacheMemoryOnly向左移动2位等于4,SDWebImageProgressiveDownload向左移动3位等于8.下面写一个& ,customImageView是我们自定义的imageView实例,在SDWebImage的SDWebImageManager.m具体使用中:&[customImageView&sd_setImageWithURL:url&placeholderImage:nil&options:SDWebImageRetryFailed&|&SDWebImageCacheMemoryOnly];注意到代码中用到了"|",‘|’是位运算中的或运算,需要两个操作数,作用是将两个数的相同位进行逻辑或运算,即如果两个对应位有一个位1,则运算后此位为1,如果两个对应位都为0。例如十进制1的二进制0b | 十进制2的二进制0b,结果为0b十进制为3。下图示例:或运算当options值为SDWebImageRetryFailed | SDWebImageCacheMemoryOnly时,执行或运算0bb = 0b 十进制是5.那么在具体的方法内部options怎么使用呢?下面的代码SD将options和SDWebImageRetryFailed做"&"运算:&&if&(url.absoluteString.length&==&0&||&(!(options&&&SDWebImageRetryFailed)&&&&isFailedUrl))&{
&&&&}‘&’是位运算中的与运算,当对应位数同为1结果才为1.例如十进制1的二进制0b&十进制2的二进制0b,结果为0b十进制是0.与运算下面代码中,SD将此时的options二进制0b和SDWebImageRetryFailed的二进制进行&运算,如果options包含了SDWebImageRetryFailed则结果为真。SD通篇都根据options做了很多事情,因此理解可选枚举和位运算非常重要。2> NSURLCredential当移动端和服务器在传输过程中,服务端有可能在返回Response时附带认证,询问 HTTP 请求的发起方是谁,这时候发起方应提供正确的用户名和密码(即认证信息)。这时候就需要NSURLCredential身份认证,更加具体可以查看。3>涉及的宏定义3-1>FOUNDATION_EXPORT:用来定义常量,和#define作用一样,只不过在检测字符串的值是否相等是比#define的效率更高,因为比较的是指针地址。3-2>NS_DESIGNATED_INITIALIZER :NS_DESIGNATED_INITIALIZER宏来实现指定构造器,通常是想告诉调用者要用这个方法去初始化类对象,便于规范API。3-2>__deprecated_msg用于提示此方法或属性已经废弃。@property&(nonatomic,&assign)&BOOL&shouldUseCredentialStorage&__deprecated_msg("Property&deprecated.&Does&nothing.&Kept&only&for&backwards&compatibility");4>initializeinitialize静态方法会在第一次使用该类之前由运行期系统调用,而且仅调用一次,属于懒加载范畴,如果不使用则不会调用,可以在方法内部做一些初始化操作,但是load方法是只要启动程序就会调用。关于initialize和load更加详细的看。5>dispatch_barrier_syncGCD中的知识点,承上启下,当把任务A添加到队列中使用dispatch_barrier_sync时,它会等待在它前面插入队列的任务先执行完,然后等任务执行完再执行后面的任务。更加详细的可点。目前整理了这些,如果还有其他必要讲的话,下文会再做解释。大家有不熟悉的知识点也可以在评论中回复,我会挑痛点比较多的及时更新到博客中。二.核心源码解析SDWebImage的核心Workflow:SDWebImage-Workflow我们要研究的源码主要是围绕这些核心类展开。阅读建议TIPS:SDWebImage的源码多,逻辑复杂,我的建议是读者下载一份源码,源码和本文同步阅读,因为如果没看过源码的调用逻辑,单看本文的解读不会形成体系。会在源码上加注释,并且copy到文章中,运行项目参考调用栈配合本文效果会更好。1>UIImageView+WebCache/UIView+WebCacheUIImageView+WebCache对外使用的API入口,这个类的接口设计把设计模式五大原则之一的接口分离原则体现的淋漓尽致。首先说一下什么是接口分离原则:接口分离原则:为特定功能提供特定的接口,不要使用单一的总接口包括所有功能,而是应该根据功能把这些接口分割,减少依赖,不能强迫用户去依赖那些他们不使用的接口。在.h中可以看到:-&(void)sd_setImageWithURL:(nullable&NSURL&*)url&NS_REFINED_FOR_SWIFT;
-&(void)sd_setImageWithURL:(nullable&NSURL&*)url
&&&&&&&&&&placeholderImage:(nullable&UIImage&*)
-&(void)sd_setImageWithURL:(nullable&NSURL&*)url
&&&&&&&&&&placeholderImage:(nullable&UIImage&*)placeholder
&&&&&&&&&&&&&&&&&&&options:(SDWebImageOptions)options这样调用者需要什么功能就去调用特定的API,清晰易扩展,在.m中会设计一个总的接口包含所有功能。UIImageView+WebCache主要是一个接口,没有太多需要琢磨的,在.m中总接口中又调用了UIView的扩展方法,接下来讲一下UIView+WebCache。UIView+WebCache提供了具体的图片加载请求,UIButton和UIImageView都可调用sd_internalSetImageWithURL来实现,下面具体看下sd_internalSetImageWithURL的实现。具体的源码意思都做了注释:-&(void)sd_internalSetImageWithURL:(nullable&NSURL&*)url
&&&&&&&&&&&&&&&&&&placeholderImage:(nullable&UIImage&*)placeholder
&&&&&&&&&&&&&&&&&&&&&&&&&&&options:(SDWebImageOptions)options
&&&&&&&&&&&&&&&&&&&&&&operationKey:(nullable&NSString&*)operationKey
&&&&&&&&&&&&&&&&&&&&&setImageBlock:(nullable&SDSetImageBlock)setImageBlock
&&&&&&&&&&&&&&&&&&&&&&&&&&progress:(nullable&SDWebImageDownloaderProgressBlock)progressBlock
&&&&&&&&&&&&&&&&&&&&&&&&&completed:(nullable&SDExternalCompletionBlock)completedBlock
&&&&&&&&&&&&&&&&&&&&&&&&&&&context:(nullable&NSDictionary&*)context&{
&&&&//根据参数operationKey取消当前类所对应的下载Operation对象,如果operationKey为nil&key取NSStringFromClass([self&class])
&&&&NSString&*validOperationKey&=&operationKey&?:&NSStringFromClass([self&class]);
&&&&//具体的取消操作在UIView+WebCacheOperation中实现
&&&&[self&sd_cancelImageLoadOperationWithKey:validOperationKey];
&&&&//利用关联对象给当前self实例绑定url&key=imageURLKey&value=url
&&&&objc_setAssociatedObject(self,&&imageURLKey,&url,&OBJC_ASSOCIATION_RETAIN_NONATOMIC);
&&&&//这里就用到了我们开篇讲的位运算,利用&与运算判断调用者是否需要设置占位图,需要则set
&&&&if&(!(options&&&SDWebImageDelayPlaceholder))&{
&&&&&&&&dispatch_main_async_safe(^{
&&&&&&&&&&&&[self&sd_setImage:placeholder&imageData:nil&basedOnClassOrViaCustomSetImageBlock:setImageBlock];
&&&&&&&&});
&&&&if&(url)&{
&&&&&&&&//&check&if&activityView&is&enabled&or&not
&&&&&&&&//&判断之前是否利用关联对象给self设置了显示菊花加载,如果有则add
&&&&&&&&if&([self&sd_showActivityIndicatorView])&{
&&&&&&&&&&&&[self&sd_addActivityIndicator];
&&&&&&&&__weak&__typeof(self)wself&=&
&&&&&&&&//调用SDWebImageManager的loadImageWithURL方法去加载图片,返回值是SDWebImageCombinedOperation
&&&&&&&&id&&operation&=&[SDWebImageManager.sharedManager&loadImageWithURL:url&options:options&progress:progressBlock&completed:^(UIImage&*image,&NSData&*data,&NSError&*error,&SDImageCacheType&cacheType,&BOOL&finished,&NSURL&*imageURL)&{
&&&&&&&&&&&&__strong&__typeof&(wself)&sself&=&
&&&&&&&&&&&&//在这里移除菊花
&&&&&&&&&&&&[sself&sd_removeActivityIndicator];
&&&&&&&&&&&&if&(!sself)&{&&}
&&&&&&&&&&&&BOOL&shouldCallCompletedBlock&=&finished&||&(options&&&SDWebImageAvoidAutoSetImage);
&&&&&&&&&&&&//是否不显示图片两个条件满足其一即可&1>调用者手动主动配置,哪怕image不为nil&2>没有图片并且不delaye占位图情况
&&&&&&&&&&&&BOOL&shouldNotSetImage&=&((image&&&&(options&&&SDWebImageAvoidAutoSetImage))&||
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&(!image&&&&!(options&&&SDWebImageDelayPlaceholder)));
&&&&&&&&&&&&SDWebImageNoParamsBlock&callCompletedBlockClojure&=&^{
&&&&&&&&&&&&&&&&if&(!sself)&{&&}
&&&&&&&&&&&&&&&&if&(!shouldNotSetImage)&{
&&&&&&&&&&&&&&&&&&&&[sself&sd_setNeedsLayout];
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&//如果设置了不自动显示图片,则回调让调用者手动添加显示图片&程序return
&&&&&&&&&&&&&&&&if&(completedBlock&&&&shouldCallCompletedBlock)&{
&&&&&&&&&&&&&&&&&&&&completedBlock(image,&error,&cacheType,&url);
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&};
&&&&&&&&&&&&
&&&&&&&&&&&&//&case&1a:&we&got&an&image,&but&the&SDWebImageAvoidAutoSetImage&flag&is&set
&&&&&&&&&&&&//&OR
&&&&&&&&&&&&//&case&1b:&we&got&no&image&and&the&SDWebImageDelayPlaceholder&is&not&set
&&&&&&&&&&&&if&(shouldNotSetImage)&{//如果设置了不自动显示图片,则回调让调用者手动添加显示图片&程序return
&&&&&&&&&&&&&&&&dispatch_main_async_safe(callCompletedBlockClojure);
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&}
&&&&&&&&&&&&
&&&&&&&&&&&&UIImage&*targetImage&=&
&&&&&&&&&&&&NSData&*targetData&=&
&&&&&&&&&&&&if&(image)&{
&&&&&&&&&&&&&&&&//&case&2a:&we&got&an&image&and&the&SDWebImageAvoidAutoSetImage&is&not&set
&&&&&&&&&&&&&&&&targetImage&=&
&&&&&&&&&&&&&&&&targetData&=&
&&&&&&&&&&&&}&else&if&(options&&&SDWebImageDelayPlaceholder)&{//如果没有image,并且调用者设置了delaye显示默认图那这里targetImage设置为placeholder
&&&&&&&&&&&&&&&&//&case&2b:&we&got&no&image&and&the&SDWebImageDelayPlaceholder&flag&is&set
&&&&&&&&&&&&&&&&targetImage&=&
&&&&&&&&&&&&&&&&targetData&=&
&&&&&&&&&&&&}
&&&&&&&&&&&&BOOL&shouldUseGlobalQueue&=&NO;
&&&&&&&&&&&&//外部参数context如果设置了全局队列中setImage,那shouldUseGlobalQueue为YES,否则默认在dispatch_get_main_queue
&&&&&&&&&&&&if&(context&&&&[context&valueForKey:SDWebImageInternalSetImageInGlobalQueueKey])&{
&&&&&&&&&&&&&&&&shouldUseGlobalQueue&=&[[context&valueForKey:SDWebImageInternalSetImageInGlobalQueueKey]&boolValue];
&&&&&&&&&&&&}
&&&&&&&&&&&&dispatch_queue_t&targetQueue&=&shouldUseGlobalQueue&?&dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,&0)&:&dispatch_get_main_queue();
&&&&&&&&&&&&
&&&&&&&&&&&&dispatch_queue_async_safe(targetQueue,&^{//队列中设置image给imageView或者button
&&&&&&&&&&&&&&&&[sself&sd_setImage:targetImage&imageData:targetData&basedOnClassOrViaCustomSetImageBlock:setImageBlock];
&&&&&&&&&&&&&&&&dispatch_main_async_safe(callCompletedBlockClojure);
&&&&&&&&&&&&});
&&&&&&&&}];
&&&&&&&&//绑定operation到当前self,key=validOperationKey,value=operation
&&&&&&&&[self&sd_setImageLoadOperation:operation&forKey:validOperationKey];
&&&&}&else&{v
&&&&&&&&dispatch_main_async_safe(^{
&&&&&&&&&&&&//移除菊花&抛出url为nil的回调
&&&&&&&&&&&&[self&sd_removeActivityIndicator];
&&&&&&&&&&&&if&(completedBlock)&{
&&&&&&&&&&&&&&&&NSError&*error&=&[NSError&errorWithDomain:SDWebImageErrorDomain&code:-1&userInfo:@{NSLocalizedDescriptionKey&:&@"Trying&to&load&a&nil&url"}];
&&&&&&&&&&&&&&&&completedBlock(nil,&error,&SDImageCacheTypeNone,&url);
&&&&&&&&&&&&}
&&&&&&&&});
}下面是在这一过程的简易流程图帮助理解:2>SDWebImageManagerSDWebImageManager类是SDWebImage中的核心类,主要负责调用SDWebImageDownloader进行图片下载,以及在下载之后利用SDImageCache进行图片缓存。并且此类还可以跳过UIImageViewe/Cache或者UIView/Cache单独使用,不仅局限于一个UIView。SDWebImageManager.h注解:@class&SDWebImageM
@protocol&SDWebImageManagerDelegate&
//当缓存没有发现当前图片,那么会查看调用者是否实现改方法,如果return一个no,则不会继续下载这张图片
-&(BOOL)imageManager:(nonnull&SDWebImageManager&*)imageManager&shouldDownloadImageForURL:(nullable&NSURL&*)imageURL;
//当图片下载完成但是未添加到缓存里面,这时候调用该方法可以给图片旋转方向,注意是异步执行,&防止组织主线程
-&(nullable&UIImage&*)imageManager:(nonnull&SDWebImageManager&*)imageManager&transformDownloadedImage:(nullable&UIImage&*)image&withURL:(nullable&NSURL&*)imageURL;
@interface&SDWebImageManager&:&NSObject
//SDWebImageManagerDelegate的delegate
@property&(weak,&nonatomic,&nullable)&id&&
//缓存中心
@property&(strong,&nonatomic,&readonly,&nullable)&SDImageCache&*imageC
//下载中心
@property&(strong,&nonatomic,&readonly,&nullable)&SDWebImageDownloader&*imageD
//这个缓存block的作用是,在block内部进行缓存key的生成并return,key就是根据图片url根据规则生成,sd的缓存策略就是key是图片url,value就是image
@property&(nonatomic,&copy,&nullable)&SDWebImageCacheKeyFilterBlock&cacheKeyF
//返回SDWebImageManager的单例
+&(nonnull&instancetype)sharedM
//根据特定的cache和downloader生成一个新的SDWebImageManager
-&(nonnull&instancetype)initWithCache:(nonnull&SDImageCache&*)cache&downloader:(nonnull&SDWebImageDownloader&*)downloader&NS_DESIGNATED_INITIALIZER;
//下载图片的关键方法,第一个参数图片url,第二个参数设置下载多样操作,第三个参数下载中进度block,第四个参数下载完成后回调
-&(nullable&id&)loadImageWithURL:(nullable&NSURL&*)url
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&options:(SDWebImageOptions)options
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&progress:(nullable&SDWebImageDownloaderProgressBlock)progressBlock
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&completed:(nullable&SDInternalCompletionBlock)completedB
//缓存图片根据指定的url和image
-&(void)saveImageToCache:(nullable&UIImage&*)image&forURL:(nullable&NSURL&*)
//取消所有当前的operation
-&(void)cancelA
//检查是否有图片正在下载
-&(BOOL)isR
//异步检查图片是否已经缓存
-&(void)cachedImageExistsForURL:(nullable&NSURL&*)url
&&&&&&&&&&&&&&&&&&&&&completion:(nullable&SDWebImageCheckCacheCompletionBlock)completionB
//检查图片是否缓存&在磁盘中
-&(void)diskImageExistsForURL:(nullable&NSURL&*)url
&&&&&&&&&&&&&&&&&&&completion:(nullable&SDWebImageCheckCacheCompletionBlock)completionB
//给定一个url返回缓存的字符串key
-&(nullable&NSString&*)cacheKeyForURL:(nullable&NSURL&*)
@endSDWebImageManager.m注解:@interface&SDWebImageCombinedOperation&:&NSObject&
//是否取消当前所有操作
@property&(assign,&nonatomic,&getter&=&isCancelled)&BOOL&
//没有参数取消回调
@property&(copy,&nonatomic,&nullable)&SDWebImageNoParamsBlock&cancelB
//执行缓存的操作
@property&(strong,&nonatomic,&nullable)&NSOperation&*cacheO
@interface&SDWebImageManager&()
//缓存对象
@property&(strong,&nonatomic,&readwrite,&nonnull)&SDImageCache&*imageC
//下载对象
@property&(strong,&nonatomic,&readwrite,&nonnull)&SDWebImageDownloader&*imageD
//集合存储所有下载失败的图片url
@property&(strong,&nonatomic,&nonnull)&NSMutableSet&*failedURLs;
//存储正在执行下载图片操作的数组
@property&(strong,&nonatomic,&nonnull)&NSMutableArray&*runningO
@implementation&SDWebImageManager
//生成一个SDWebImagemanager的单例
+&(nonnull&instancetype)sharedManager&{
&&&&static&dispatch_once_t&
&&&&static&id&
&&&&dispatch_once(&once,&^{
&&&&&&&&instance&=&[self&new];
&&&&return&
//初始化SDImageCache/SDWebImageDownloade
-&(nonnull&instancetype)init&{
&&&&SDImageCache&*cache&=&[SDImageCache&sharedImageCache];
&&&&SDWebImageDownloader&*downloader&=&[SDWebImageDownloader&sharedDownloader];
&&&&return&[self&initWithCache:cache&downloader:downloader];
//初始化以及属性绑定
-&(nonnull&instancetype)initWithCache:(nonnull&SDImageCache&*)cache&downloader:(nonnull&SDWebImageDownloader&*)downloader&{
&&&&if&((self&=&[super&init]))&{
&&&&&&&&_imageCache&=&
&&&&&&&&_imageDownloader&=&
&&&&&&&&_failedURLs&=&[NSMutableSet&new];
&&&&&&&&_runningOperations&=&[NSMutableArray&new];
&&&&return&
//根据URL获取缓存中的key
-&(nullable&NSString&*)cacheKeyForURL:(nullable&NSURL&*)url&{
&&&&if&(!url)&{
&&&&&&&&return&@"";
&&&&if&(self.cacheKeyFilter)&{
&&&&&&&&return&self.cacheKeyFilter(url);
&&&&}&else&{
&&&&&&&&return&url.absoluteS
//检查缓存中是否缓存了当前url对应的图片-先判断内存缓存、再判断磁盘缓存
-&(void)cachedImageExistsForURL:(nullable&NSURL&*)url
&&&&&&&&&&&&&&&&&&&&&completion:(nullable&SDWebImageCheckCacheCompletionBlock)completionBlock&{
&&&&NSString&*key&=&[self&cacheKeyForURL:url];
&&&&//判断内存缓存是否存在
&&&&BOOL&isInMemoryCache&=&([self.imageCache&imageFromMemoryCacheForKey:key]&!=&nil);
&&&&if&(isInMemoryCache)&{
&&&&&&&&//&making&sure&we&call&the&completion&block&on&the&main&queue
&&&&&&&&dispatch_async(dispatch_get_main_queue(),&^{
&&&&&&&&&&&&if&(completionBlock)&{
&&&&&&&&&&&&&&&&completionBlock(YES);
&&&&&&&&&&&&}
&&&&&&&&});
&&&&//判断磁盘缓存中是否存在
&&&&[self.imageCache&diskImageExistsWithKey:key&completion:^(BOOL&isInDiskCache)&{
&&&&&&&&//&the&completion&block&of&checkDiskCacheForImageWithKey:completion:&is&always&called&on&the&main&queue,&no&need&to&further&dispatch
&&&&&&&&if&(completionBlock)&{
&&&&&&&&&&&&completionBlock(isInDiskCache);
//根据URL判断磁盘缓存中是否存在图片
-&(void)diskImageExistsForURL:(nullable&NSURL&*)url
&&&&&&&&&&&&&&&&&&&completion:(nullable&SDWebImageCheckCacheCompletionBlock)completionBlock&{
&&&&NSString&*key&=&[self&cacheKeyForURL:url];
&&&&[self.imageCache&diskImageExistsWithKey:key&completion:^(BOOL&isInDiskCache)&{
&&&&&&&&//&the&completion&block&of&checkDiskCacheForImageWithKey:completion:&is&always&called&on&the&main&queue,&no&need&to&further&dispatch
&&&&&&&&if&(completionBlock)&{
&&&&&&&&&&&&completionBlock(isInDiskCache);
//进行图片下载操作
-&(id&)loadImageWithURL:(nullable&NSURL&*)url
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&options:(SDWebImageOptions)options
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&progress:(nullable&SDWebImageDownloaderProgressBlock)progressBlock
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&completed:(nullable&SDInternalCompletionBlock)completedBlock&{
&&&&//&Invoking&this&method&without&a&completedBlock&is&pointless
&&&&//completedBlock为nil,则触发断言,程序crash
&&&&NSAssert(completedBlock&!=&nil,&@"If&you&mean&to&prefetch&the&image,&use&-[SDWebImagePrefetcher&prefetchURLs]&instead");
&&&&//&Very&common&mistake&is&to&send&the&URL&using&NSString&object&instead&of&NSURL.&For&some&strange&reason,&Xcode&won&#39;t
&&&&//&throw&any&warning&for&this&type&mismatch.&Here&we&failsafe&this&error&by&allowing&URLs&to&be&passed&as&NSString.
&&&&if&([url&isKindOfClass:NSString.class])&{
&&&&&&&&url&=&[NSURL&URLWithString:(NSString&*)url];
&&&&//&Prevents&app&crashing&on&argument&type&error&like&sending&NSNull&instead&of&NSURL
&&&&if&(![url&isKindOfClass:NSURL.class])&{
&&&&&&&&url&=&
&&&&//封装下载操作的对象
&&&&__block&SDWebImageCombinedOperation&*operation&=&[SDWebImageCombinedOperation&new];
&&&&__weak&SDWebImageCombinedOperation&*weakOperation&=&
&&&&BOOL&isFailedUrl&=&NO;
&&&&if&(url)&{
&&&&&&&&//为了防止在多线程访问出现问题,创建互斥锁
&&&&&&&&@synchronized&(self.failedURLs)&{
&&&&&&&&&&&&isFailedUrl&=&[self.failedURLs&containsObject:url];
&&&&//如果url为nil,或者没有设置失败url重新下载的配置且该url已经下载失败过,那么返回失败的回调
&&&&if&(url.absoluteString.length&==&0&||&(!(options&&&SDWebImageRetryFailed)&&&&isFailedUrl))&{
&&&&&&&&[self&callCompletionBlockForOperation:operation&completion:completedBlock&error:[NSError&errorWithDomain:NSURLErrorDomain&code:NSURLErrorFileDoesNotExist&userInfo:nil]&url:url];
&&&&&&&&return&
&&&&//创建互斥锁,添加operation到数组中
&&&&@synchronized&(self.runningOperations)&{
&&&&&&&&[self.runningOperations&addObject:operation];
&&&&NSString&*key&=&[self&cacheKeyForURL:url];
&&&&//使用缓存对象,根据key去寻找查找
&&&&operation.cacheOperation&=&[self.imageCache&queryCacheOperationForKey:key&done:^(UIImage&*cachedImage,&NSData&*cachedData,&SDImageCacheType&cacheType)&{
&&&&&&&&//如果当前操作被取消,则remove且return
&&&&&&&&if&(operation.isCancelled)&{
&&&&&&&&&&&&[self&safelyRemoveOperationFromRunning:operation];
&&&&&&&&&&&&
&&&&&&&&//(如果没有图片缓存或者设置了重新刷新缓存)且调用代理允许下载图片
&&&&&&&&if&((!cachedImage&||&options&&&SDWebImageRefreshCached)&&&&(![self.delegate&respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)]&||&[self.delegate&imageManager:self&shouldDownloadImageForURL:url]))&{
&&&&&&&&&&&&//如果有(其实是缓存的)并且调用者设置了重新刷新缓存,那么先把图片结果回调出去,然后继续去下载图片再更新缓存
&&&&&&&&&&&&if&(cachedImage&&&&options&&&SDWebImageRefreshCached)&{
&&&&&&&&&&&&&&&&//&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.
&&&&&&&&&&&&&&&&[self&callCompletionBlockForOperation:weakOperation&completion:completedBlock&image:cachedImage&data:cachedData&error:nil&cacheType:cacheType&finished:YES&url:url];
&&&&&&&&&&&&}
&&&&&&&&&&&&//&download&if&no&image&or&requested&to&refresh&anyway,&and&download&allowed&by&delegate
&&&&&&&&&&&&//如果缓存没有图片或者请求刷新,并且通过代理下载图片,那么则下载图片
&&&&&&&&&&&&//下面是根据调用者传进来的option,来匹配设置了哪些,就给downloaderOptions赋值哪些option
&&&&&&&&&&&&SDWebImageDownloaderOptions&downloaderOptions&=&0;
&&&&&&&&&&&&if&(options&&&SDWebImageLowPriority)&downloaderOptions&|=&SDWebImageDownloaderLowP
&&&&&&&&&&&&if&(options&&&SDWebImageProgressiveDownload)&downloaderOptions&|=&SDWebImageDownloaderProgressiveD
&&&&&&&&&&&&if&(options&&&SDWebImageRefreshCached)&downloaderOptions&|=&SDWebImageDownloaderUseNSURLC
&&&&&&&&&&&&if&(options&&&SDWebImageContinueInBackground)&downloaderOptions&|=&SDWebImageDownloaderContinueInB
&&&&&&&&&&&&if&(options&&&SDWebImageHandleCookies)&downloaderOptions&|=&SDWebImageDownloaderHandleC
&&&&&&&&&&&&if&(options&&&SDWebImageAllowInvalidSSLCertificates)&downloaderOptions&|=&SDWebImageDownloaderAllowInvalidSSLC
&&&&&&&&&&&&if&(options&&&SDWebImageHighPriority)&downloaderOptions&|=&SDWebImageDownloaderHighP
&&&&&&&&&&&&if&(options&&&SDWebImageScaleDownLargeImages)&downloaderOptions&|=&SDWebImageDownloaderScaleDownLargeI
&&&&&&&&&&&&
&&&&&&&&&&&&if&(cachedImage&&&&options&&&SDWebImageRefreshCached)&{
&&&&&&&&&&&&&&&&//&force&progressive&off&if&image&already&cached&but&forced&refreshing
&&&&&&&&&&&&&&&&downloaderOptions&&=&~SDWebImageDownloaderProgressiveD
&&&&&&&&&&&&&&&&//&ignore&image&read&from&NSURLCache&if&image&if&cached&but&force&refreshing
&&&&&&&&&&&&&&&&downloaderOptions&|=&SDWebImageDownloaderIgnoreCachedR
&&&&&&&&&&&&}
&&&&&&&&&&&&//在这里真正调用imageDownloader去下载图片了
&&&&&&&&&&&&SDWebImageDownloadToken&*subOperationToken&=&[self.imageDownloader&downloadImageWithURL:url&options:downloaderOptions&progress:progressBlock&completed:^(UIImage&*downloadedImage,&NSData&*downloadedData,&NSError&*error,&BOOL&finished)&{
&&&&&&&&&&&&&&&&__strong&__typeof(weakOperation)&strongOperation&=&weakO
&&&&&&&&&&&&&&&&//操作取消则不做任何处理
&&&&&&&&&&&&&&&&if&(!strongOperation&||&strongOperation.isCancelled)&{
&&&&&&&&&&&&&&&&&&&&//&Do&nothing&if&the&operation&was&cancelled
&&&&&&&&&&&&&&&&&&&&//&See&#699&for&more&details
&&&&&&&&&&&&&&&&&&&&//&if&we&would&call&the&completedBlock,&there&could&be&a&race&condition&between&this&block&and&another&completedBlock&for&the&same&object,&so&if&this&one&is&called&second,&we&will&overwrite&the&new&data
&&&&&&&&&&&&&&&&}&else&if&(error)&{
&&&&&&&&&&&&&&&&&&&&[self&callCompletionBlockForOperation:strongOperation&completion:completedBlock&error:error&url:url];
&&&&&&&&&&&&&&&&&&&&if&(&&&error.code&!=&NSURLErrorNotConnectedToInternet
&&&&&&&&&&&&&&&&&&&&&&&&&&&error.code&!=&NSURLErrorCancelled
&&&&&&&&&&&&&&&&&&&&&&&&&&&error.code&!=&NSURLErrorTimedOut
&&&&&&&&&&&&&&&&&&&&&&&&&&&error.code&!=&NSURLErrorInternationalRoamingOff
&&&&&&&&&&&&&&&&&&&&&&&&&&&error.code&!=&NSURLErrorDataNotAllowed
&&&&&&&&&&&&&&&&&&&&&&&&&&&error.code&!=&NSURLErrorCannotFindHost
&&&&&&&&&&&&&&&&&&&&&&&&&&&error.code&!=&NSURLErrorCannotConnectToHost
&&&&&&&&&&&&&&&&&&&&&&&&&&&error.code&!=&NSURLErrorNetworkConnectionLost)&{
&&&&&&&&&&&&&&&&&&&&&&&&//下载失败则添加图片url到failedURLs集合
&&&&&&&&&&&&&&&&&&&&&&&&@synchronized&(self.failedURLs)&{
&&&&&&&&&&&&&&&&&&&&&&&&&&&&[self.failedURLs&addObject:url];
&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&else&{
&&&&&&&&&&&&&&&&&&&&//虽然下载失败,但是如果设置了可以重新下载失败的url则remove该url
&&&&&&&&&&&&&&&&&&&&if&((options&&&SDWebImageRetryFailed))&{
&&&&&&&&&&&&&&&&&&&&&&&&@synchronized&(self.failedURLs)&{
&&&&&&&&&&&&&&&&&&&&&&&&&&&&[self.failedURLs&removeObject:url];
&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&//是否需要缓存在磁盘
&&&&&&&&&&&&&&&&&&&&BOOL&cacheOnDisk&=&!(options&&&SDWebImageCacheMemoryOnly);
&&&&&&&&&&&&&&&&&&&&if&(options&&&SDWebImageRefreshCached&&&&cachedImage&&&&!downloadedImage)&{
&&&&&&&&&&&&&&&&&&&&&&&&//&Image&refresh&hit&the&NSURLCache&cache,&do&not&call&the&completion&block
&&&&&&&&&&&&&&&&&&&&//图片下载成功并且判断是否需要转换图片
&&&&&&&&&&&&&&&&&&&&}&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];
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&//&pass&nil&if&the&image&was&transformed,&so&we&can&recalculate&the&data&from&the&image
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&[self.imageCache&storeImage:transformedImage&imageData:(imageWasTransformed&?&nil&:&downloadedData)&forKey:key&toDisk:cacheOnDisk&completion:nil];
&&&&&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&[self&callCompletionBlockForOperation:strongOperation&completion:completedBlock&image:transformedImage&data:downloadedData&error:nil&cacheType:SDImageCacheTypeNone&finished:finished&url:url];
&&&&&&&&&&&&&&&&&&&&&&&&});
&&&&&&&&&&&&&&&&&&&&}&else&{
&&&&&&&&&&&&&&&&&&&&&&&&//下载完成且有image则缓存图片
&&&&&&&&&&&&&&&&&&&&&&&&if&(downloadedImage&&&&finished)&{
&&&&&&&&&&&&&&&&&&&&&&&&&&&&[self.imageCache&storeImage:downloadedImage&imageData:downloadedData&forKey:key&toDisk:cacheOnDisk&completion:nil];
&&&&&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&&&&&&&&&[self&callCompletionBlockForOperation:strongOperation&completion:completedBlock&image:downloadedImage&data:downloadedData&error:nil&cacheType:SDImageCacheTypeNone&finished:finished&url:url];
&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&//如果下载和缓存都完成了则删除操作队列中的operation
&&&&&&&&&&&&&&&&if&(finished)&{
&&&&&&&&&&&&&&&&&&&&[self&safelyRemoveOperationFromRunning:strongOperation];
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}];
&&&&&&&&&&&&@synchronized(operation)&{
&&&&&&&&&&&&&&&&//&Need&same&lock&to&ensure&cancelBlock&called&because&cancel&method&can&be&called&in&different&queue
&&&&&&&&&&&&&&&&operation.cancelBlock&=&^{
&&&&&&&&&&&&&&&&&&&&[self.imageDownloader&cancel:subOperationToken];
&&&&&&&&&&&&&&&&&&&&__strong&__typeof(weakOperation)&strongOperation&=&weakO
&&&&&&&&&&&&&&&&&&&&[self&safelyRemoveOperationFromRunning:strongOperation];
&&&&&&&&&&&&&&&&};
&&&&&&&&&&&&}
&&&&&&&&}&else&if&(cachedImage)&{
&&&&&&&&&&&&//&有图片且线程没有被取消,则返回有图片的completedBlock
&&&&&&&&&&&&__strong&__typeof(weakOperation)&strongOperation&=&weakO
&&&&&&&&&&&&[self&callCompletionBlockForOperation:strongOperation&completion:completedBlock&image:cachedImage&data:cachedData&error:nil&cacheType:cacheType&finished:YES&url:url];
&&&&&&&&&&&&[self&safelyRemoveOperationFromRunning:operation];
&&&&&&&&}&else&{
&&&&&&&&&&&&//&Image&not&in&cache&and&download&disallowed&by&delegate
&&&&&&&&&&&&//没有在缓存中并且代理方法也不允许下载则回调失败
&&&&&&&&&&&&__strong&__typeof(weakOperation)&strongOperation&=&weakO
&&&&&&&&&&&&[self&callCompletionBlockForOperation:strongOperation&completion:completedBlock&image:nil&data:nil&error:nil&cacheType:SDImageCacheTypeNone&finished:YES&url:url];
&&&&&&&&&&&&[self&safelyRemoveOperationFromRunning:operation];
&&&&return&
//将图片存入缓存
-&(void)saveImageToCache:(nullable&UIImage&*)image&forURL:(nullable&NSURL&*)url&{
&&&&if&(image&&&&url)&{
&&&&&&&&NSString&*key&=&[self&cacheKeyForURL:url];
&&&&&&&&[self.imageCache&storeImage:image&forKey:key&toDisk:YES&completion:nil];
//取消所有的下载操作
-&(void)cancelAll&{
&&&&@synchronized&(self.runningOperations)&{
&&&&&&&&NSArray&*copiedOperations&=&[self.runningOperations&copy];
&&&&&&&&[copiedOperations&makeObjectsPerformSelector:@selector(cancel)];
&&&&&&&&[self.runningOperations&removeObjectsInArray:copiedOperations];
//判断当前是否有下载图片
-&(BOOL)isRunning&{
&&&&BOOL&isRunning&=&NO;
&&&&@synchronized&(self.runningOperations)&{
&&&&&&&&isRunning&=&(self.runningOperations.count&>&0);
&&&&return&isR
//线程安全的移除下载operation
-&(void)safelyRemoveOperationFromRunning:(nullable&SDWebImageCombinedOperation*)operation&{
&&&&@synchronized&(self.runningOperations)&{
&&&&&&&&if&(operation)&{
&&&&&&&&&&&&[self.runningOperations&removeObject:operation];
-&(void)callCompletionBlockForOperation:(nullable&SDWebImageCombinedOperation*)operation
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&completion:(nullable&SDInternalCompletionBlock)completionBlock
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&error:(nullable&NSError&*)error
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&url:(nullable&NSURL&*)url&{
&&&&[self&callCompletionBlockForOperation:operation&completion:completionBlock&image:nil&data:nil&error:error&cacheType:SDImageCacheTypeNone&finished:YES&url:url];
-&(void)callCompletionBlockForOperation:(nullable&SDWebImageCombinedOperation*)operation
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&completion:(nullable&SDInternalCompletionBlock)completionBlock
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&image:(nullable&UIImage&*)image
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&data:(nullable&NSData&*)data
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&error:(nullable&NSError&*)error
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&cacheType:(SDImageCacheType)cacheType
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&finished:(BOOL)finished
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&url:(nullable&NSURL&*)url&{
&&&&dispatch_main_async_safe(^{
&&&&&&&&if&(operation&&&&!operation.isCancelled&&&&completionBlock)&{
&&&&&&&&&&&&completionBlock(image,&data,&error,&cacheType,&finished,&url);
@implementation&SDWebImageCombinedOperation
-&(void)setCancelBlock:(nullable&SDWebImageNoParamsBlock)cancelBlock&{
&&&&//&check&if&the&operation&is&already&cancelled,&then&we&just&call&the&cancelBlock
&&&&if&(self.isCancelled)&{
&&&&&&&&if&(cancelBlock)&{
&&&&&&&&&&&&cancelBlock();
&&&&&&&&_cancelBlock&=&&//&don&#39;t&forget&to&nil&the&cancelBlock,&otherwise&we&will&get&crashes
&&&&}&else&{
&&&&&&&&_cancelBlock&=&[cancelBlock&copy];
//SDWebImageOperation协议方法
-&(void)cancel&{
&&&&@synchronized(self)&{
&&&&&&&&self.cancelled&=&YES;
&&&&&&&&if&(self.cacheOperation)&{
&&&&&&&&&&&&[self.cacheOperation&cancel];
&&&&&&&&&&&&self.cacheOperation&=&
&&&&&&&&if&(self.cancelBlock)&{
&&&&&&&&&&&&self.cancelBlock();
&&&&&&&&&&&&self.cancelBlock&=&
@end下面是SDWebImageManager的关键节点流程图:3>SDImageCacheSDImageCache是SDWebImage的缓存中心。分三部分组成memory内存缓存,disk硬盘缓存和无缓存组成。SDImageCache.h文件注解:typedef&NS_ENUM(NSInteger,&SDImageCacheType)&{
&&&&无缓存类型
&&&&SDImageCacheTypeNone,
&&&&磁盘缓存
&&&&SDImageCacheTypeDisk,
&&&&内存缓存
&&&&SDImageCacheTypeMemory
@interface&SDImageCache&:&NSObject
#pragma&mark&-&Properties
//缓存配置对象,包含所有配置项
@property&(nonatomic,&nonnull,&readonly)&SDImageCacheConfig&*
//设置内存容量大小
@property&(assign,&nonatomic)&NSUInteger&maxMemoryC
//设置内存缓存最大值limit
@property&(assign,&nonatomic)&NSUInteger&maxMemoryCountL
#pragma&mark&-&Singleton&and&initialization
//返回SDImageCache单例
+&(nonnull&instancetype)sharedImageC
//根据特定的namespace返回一个新的缓存对象
-&(nonnull&instancetype)initWithNamespace:(nonnull&NSString&*)
//根据特定的namespace和directory返回一个新的缓存对象
-&(nonnull&instancetype)initWithNamespace:(nonnull&NSString&*)ns
&&&&&&&&&&&&&&&&&&&&&&&diskCacheDirectory:(nonnull&NSString&*)directory&NS_DESIGNATED_INITIALIZER;
#pragma&mark&-&Cache&paths
//生成缓存路径
-&(nullable&NSString&*)makeDiskCachePath:(nonnull&NSString*)fullN
//添加一个只读的缓存路径
-&(void)addReadOnlyCachePath:(nonnull&NSString&*)
#pragma&mark&-&Store&Ops
//根据key去异步缓存image,缓存在内存和磁盘
-&(void)storeImage:(nullable&UIImage&*)image
&&&&&&&&&&&&forKey:(nullable&NSString&*)key
&&&&&&&&completion:(nullable&SDWebImageNoParamsBlock)completionB
//根据key去异步缓存image,toDisk未NO不存储在磁盘
-&(void)storeImage:(nullable&UIImage&*)image
&&&&&&&&&&&&forKey:(nullable&NSString&*)key
&&&&&&&&&&&&toDisk:(BOOL)toDisk
&&&&&&&&completion:(nullable&SDWebImageNoParamsBlock)completionB
//根据key去异步缓存image,toDisk未NO不存储在磁盘&多加一个imageData图片data
-&(void)storeImage:(nullable&UIImage&*)image
&&&&&&&&&imageData:(nullable&NSData&*)imageData
&&&&&&&&&&&&forKey:(nullable&NSString&*)key
&&&&&&&&&&&&toDisk:(BOOL)toDisk
&&&&&&&&completion:(nullable&SDWebImageNoParamsBlock)completionB
//根据key去异步缓存image,缓存在磁盘
-&(void)storeImageDataToDisk:(nullable&NSData&*)imageData&forKey:(nullable&NSString&*)
#pragma&mark&-&Query&and&Retrieve&Ops
//异步检查图片是否缓存在磁盘中
-&(void)diskImageExistsWithKey:(nullable&NSString&*)key&completion:(nullable&SDWebImageCheckCacheCompletionBlock)completionB
//在缓存中查询对应key的数据
-&(nullable&NSOperation&*)queryCacheOperationForKey:(nullable&NSString&*)key&done:(nullable&SDCacheQueryCompletedBlock)doneB
//在内存缓存中查询对应key的图片
-&(nullable&UIImage&*)imageFromMemoryCacheForKey:(nullable&NSString&*)
//在磁盘缓存中查询对应key的图片
-&(nullable&UIImage&*)imageFromDiskCacheForKey:(nullable&NSString&*)
//在缓存中查询对应key的图片
-&(nullable&UIImage&*)imageFromCacheForKey:(nullable&NSString&*)
#pragma&mark&-&Remove&Ops
//删除缓存中指定key的图片
-&(void)removeImageForKey:(nullable&NSString&*)key&withCompletion:(nullable&SDWebImageNoParamsBlock)
//删除缓存中指定key的图片&磁盘是可选项
-&(void)removeImageForKey:(nullable&NSString&*)key&fromDisk:(BOOL)fromDisk&withCompletion:(nullable&SDWebImageNoParamsBlock)
#pragma&mark&-&Cache&clean&Ops
//清空所有的内存缓存
-&(void)clearM
//异步清除所有的磁盘缓存
-&(void)clearDiskOnCompletion:(nullable&SDWebImageNoParamsBlock)
//异步清除所有的失效的缓存图片-因为可以设定缓存时间,超过则失效
-&(void)deleteOldFilesWithCompletionBlock:(nullable&SDWebImageNoParamsBlock)completionB
#pragma&mark&-&Cache&Info
//得到磁盘缓存的大小size
-&(NSUInteger)getS
//得到在磁盘缓存中图片的数量
-&(NSUInteger)getDiskC
//异步计算磁盘缓存的大小
-&(void)calculateSizeWithCompletionBlock:(nullable&SDWebImageCalculateSizeBlock)completionB
#pragma&mark&-&Cache&Paths
//获取给定key的缓存路径&需要一个根缓存路径
-&(nullable&NSString&*)cachePathForKey:(nullable&NSString&*)key&inPath:(nonnull&NSString&*)
//获取给定key的默认缓存路径
-&(nullable&NSString&*)defaultCachePathForKey:(nullable&NSString&*)
@endSDImageCache.m注解://&See&https://github.com/rs/SDWebImage/pull/1141&for&discussion
//自定义内存缓存类
@interface&AutoPurgeCache&:&NSCache
@implementation&AutoPurgeCache
-&(nonnull&instancetype)init&{
&&&&self&=&[super&init];
&&&&if&(self)&{
#if&SD_UIKIT
&&&&&&&&//添加通知,当受到内存警告则移除所有的缓存对象
&&&&&&&&[[NSNotificationCenter&defaultCenter]&addObserver:self&selector:@selector(removeAllObjects)&name:UIApplicationDidReceiveMemoryWarningNotification&object:nil];
&&&&return&
-&(void)dealloc&{
#if&SD_UIKIT
&&&&[[NSNotificationCenter&defaultCenter]&removeObserver:self&name:UIApplicationDidReceiveMemoryWarningNotification&object:nil];
//内联函数获得该图片的缓存大小&注意乘以屏幕的比例
FOUNDATION_STATIC_INLINE&NSUInteger&SDCacheCostForImage(UIImage&*image)&{
#if&SD_MAC
&&&&return&image.size.height&*&image.size.
#elif&SD_UIKIT&||&SD_WATCH
&&&&return&image.size.height&*&image.size.width&*&image.scale&*&image.
@interface&SDImageCache&()
#pragma&mark&-&Properties
//内存缓存对象
@property&(strong,&nonatomic,&nonnull)&NSCache&*memC
//磁盘缓存路径
@property&(strong,&nonatomic,&nonnull)&NSString&*diskCacheP
//保存缓存路径的数组
@property&(strong,&nonatomic,&nullable)&NSMutableArray&*customP
//执行处理输入输出的队列
@property&(SDDispatchQueueSetterSementics,&nonatomic,&nullable)&dispatch_queue_t&ioQ
@implementation&SDImageCache&{
&&&&NSFileManager&*_fileM
#pragma&mark&-&Singleton,&init,&dealloc
//生成单例SDImageCache
+&(nonnull&instancetype)sharedImageCache&{
&&&&static&dispatch_once_t&
&&&&static&id&
&&&&dispatch_once(&once,&^{
&&&&&&&&instance&=&[self&new];
&&&&return&
-&(instancetype)init&{
&&&&return&[self&initWithNamespace:@"default"];
-&(nonnull&instancetype)initWithNamespace:(nonnull&NSString&*)ns&{
&&&&NSString&*path&=&[self&makeDiskCachePath:ns];
&&&&return&[self&initWithNamespace:ns&diskCacheDirectory:path];
//初始化磁盘缓存路径和内存缓存name
-&(nonnull&instancetype)initWithNamespace:(nonnull&NSString&*)ns
&&&&&&&&&&&&&&&&&&&&&&&diskCacheDirectory:(nonnull&NSString&*)directory&{
&&&&if&((self&=&[super&init]))&{
&&&&&&&&NSString&*fullNamespace&=&[@"com.hackemist.SDWebImageCache."&stringByAppendingString:ns];
&&&&&&&&//&Create&IO&serial&queue
&&&&&&&&_ioQueue&=&dispatch_queue_create("com.hackemist.SDWebImageCache",&DISPATCH_QUEUE_SERIAL);
&&&&&&&&_config&=&[[SDImageCacheConfig&alloc]&init];
&&&&&&&&//&Init&the&memory&cache
&&&&&&&&_memCache&=&[[AutoPurgeCache&alloc]&init];
&&&&&&&&_memCache.name&=&fullN
&&&&&&&&//&Init&the&disk&cache
&&&&&&&&if&(directory&!=&nil)&{
&&&&&&&&&&&&_diskCachePath&=&[directory&stringByAppendingPathComponent:fullNamespace];
&&&&&&&&}&else&{
&&&&&&&&&&&&NSString&*path&=&[self&makeDiskCachePath:ns];
&&&&&&&&&&&&_diskCachePath&=&
&&&&&&&&dispatch_sync(_ioQueue,&^{
&&&&&&&&&&&&_fileManager&=&[NSFileManager&new];
&&&&&&&&});
#if&SD_UIKIT
&&&&&&&&//&Subscribe&to&app&events
&&&&&&&&[[NSNotificationCenter&defaultCenter]&addObserver:self
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&selector:@selector(clearMemory)
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&name:UIApplicationDidReceiveMemoryWarningNotification
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&object:nil];
&&&&&&&&[[NSNotificationCenter&defaultCenter]&addObserver:self
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&selector:@selector(deleteOldFiles)
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&name:UIApplicationWillTerminateNotification
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&object:nil];
&&&&&&&&[[NSNotificationCenter&defaultCenter]&addObserver:self
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&selector:@selector(backgroundDeleteOldFiles)
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&name:UIApplicationDidEnterBackgroundNotification
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&object:nil];
&&&&return&
-&(void)dealloc&{
&&&&[[NSNotificationCenter&defaultCenter]&removeObserver:self];
&&&&SDDispatchQueueRelease(_ioQueue);
-&(void)checkIfQueueIsIOQueue&{
&&&&//dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)获取当前队列的名字
&&&&//dispatch_queue_get_label获取队列的名字,如果队列没有名字,返回NULL
&&&&const&char&*currentQueueLabel&=&dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL);
&&&&const&char&*ioQueueLabel&=&dispatch_queue_get_label(self.ioQueue);
&&&&if&(strcmp(currentQueueLabel,&ioQueueLabel)&!=&0)&{
&&&&&&&&NSLog(@"This&method&should&be&called&from&the&ioQueue");
#pragma&mark&-&Cache&paths
////添加一个只读的缓存路径
-&(void)addReadOnlyCachePath:(nonnull&NSString&*)path&{
&&&&if&(!self.customPaths)&{
&&&&&&&&self.customPaths&=&[NSMutableArray&new];
&&&&if&(![self.customPaths&containsObject:path])&{
&&&&&&&&[self.customPaths&addObject:path];
//获取给定key的缓存路径&需要一个根缓存路径
-&(nullable&NSString&*)cachePathForKey:(nullable&NSString&*)key&inPath:(nonnull&NSString&*)path&{
&&&&NSString&*filename&=&[self&cachedFileNameForKey:key];
&&&&return&[path&stringByAppendingPathComponent:filename];
//获取给定key的默认缓存路径
-&(nullable&NSString&*)defaultCachePathForKey:(nullable&NSString&*)key&{
&&&&return&[self&cachePathForKey:key&inPath:self.diskCachePath];
//根据key值生成文件名:采用MD5
-&(nullable&NSString&*)cachedFileNameForKey:(nullable&NSString&*)key&{
&&&&const&char&*str&=&key.UTF8S
&&&&if&(str&==&NULL)&{
&&&&&&&&str&=&"";
&&&&unsigned&char&r[CC_MD5_DIGEST_LENGTH];
&&&&CC_MD5(str,&(CC_LONG)strlen(str),&r);
&&&&NSURL&*keyURL&=&[NSURL&URLWithString:key];
&&&&NSString&*ext&=&keyURL&?&keyURL.pathExtension&:&key.pathE
&&&&NSString&*filename&=&[NSString&stringWithFormat:@"xxxxxxxxxxxxxxxx%@",
&&&&&&&&&&&&&&&&&&&&&&&&&&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],&ext.length&==&0&?&@""&:&[NSString&stringWithFormat:@".%@",&ext]];
&&&&return&
//生成磁盘路径
-&(nullable&NSString&*)makeDiskCachePath:(nonnull&NSString*)fullNamespace&{
&&&&NSArray&*paths&=&NSSearchPathForDirectoriesInDomains(NSCachesDirectory,&NSUserDomainMask,&YES);
&&&&return&[paths[0]&stringByAppendingPathComponent:fullNamespace];
#pragma&mark&-&Store&Ops
-&(void)storeImage:(nullable&UIImage&*)image
&&&&&&&&&&&&forKey:(nullable&NSString&*)key
&&&&&&&&completion:(nullable&SDWebImageNoParamsBlock)completionBlock&{
&&&&[self&storeImage:image&imageData:nil&forKey:key&toDisk:YES&completion:completionBlock];
-&(void)storeImage:(nullable&UIImage&*)image
&&&&&&&&&&&&forKey:(nullable&NSString&*)key
&&&&&&&&&&&&toDisk:(BOOL)toDisk
&&&&&&&&completion:(nullable&SDWebImageNoParamsBlock)completionBlock&{
&&&&[self&storeImage:image&imageData:nil&forKey:key&toDisk:toDisk&completion:completionBlock];
-&(void)storeImage:(nullable&UIImage&*)image
&&&&&&&&&imageData:(nullable&NSData&*)imageData
&&&&&&&&&&&&forKey:(nullable&NSString&*)key
&&&&&&&&&&&&toDisk:(BOOL)toDisk
&&&&&&&&completion:(nullable&SDWebImageNoParamsBlock)completionBlock&{
&&&&if&(!image&||&!key)&{
&&&&&&&&if&(completionBlock)&{
&&&&&&&&&&&&completionBlock();
&&&&//&if&memory&cache&is&enabled
&&&&//缓存到内存
&&&&if&(self.config.shouldCacheImagesInMemory)&{
&&&&&&&&NSUInteger&cost&=&SDCacheCostForImage(image);
&&&&&&&&[self.memCache&setObject:image&forKey:key&cost:cost];
&&&&//缓存到磁盘,采用异步操作
&&&&if&(toDisk)&{
&&&&&&&&dispatch_async(self.ioQueue,&^{
&&&&&&&&&&&&@autoreleasepool&{
&&&&&&&&&&&&&&&&NSData&*data&=&imageD
&&&&&&&&&&&&&&&&if&(!data&&&&image)&{
&&&&&&&&&&&&&&&&&&&&//&If&we&do&not&have&any&data&to&detect&image&format,&use&PNG&format
&&&&&&&&&&&&&&&&&&&&//如果没有data则采用png的格式进行format
&&&&&&&&&&&&&&&&&&&&data&=&[[SDWebImageCodersManager&sharedInstance]&encodedDataWithImage:image&format:SDImageFormatPNG];
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&[self&storeImageDataToDisk:data&forKey:key];
&&&&&&&&&&&&}
&&&&&&&&&&&&
&&&&&&&&&&&&if&(completionBlock)&{
&&&&&&&&&&&&&&&&dispatch_async(dispatch_get_main_queue(),&^{
&&&&&&&&&&&&&&&&&&&&completionBlock();
&&&&&&&&&&&&&&&&});
&&&&&&&&&&&&}
&&&&&&&&});
&&&&}&else&{
&&&&&&&&if&(completionBlock)&{
&&&&&&&&&&&&completionBlock();
//利用key进行缓存data
-&(void)storeImageDataToDisk:(nullable&NSData&*)imageData&forKey:(nullable&NSString&*)key&{
&&&&if&(!imageData&||&!key)&{
&&&&[self&checkIfQueueIsIOQueue];
&&&&//如果文件中不存在磁盘缓存路径&则创建
&&&&if&(![_fileManager&fileExistsAtPath:_diskCachePath])&{
&&&&&&&&[_fileManager&createDirectoryAtPath:_diskCachePath&withIntermediateDirectories:YES&attributes:nil&error:NULL];
&&&&//&get&cache&Path&for&image&key&&得到该key的缓存路径
&&&&NSString&*cachePathForKey&=&[self&defaultCachePathForKey:key];
&&&&//&transform&to&NSUrl&&将缓存路径转化为url
&&&&NSURL&*fileURL&=&[NSURL&fileURLWithPath:cachePathForKey];
&&&&//将imageData存储起来
&&&&[_fileManager&createFileAtPath:cachePathForKey&contents:imageData&attributes:nil];
&&&&//&disable&iCloud&backup&&如果调用者关闭icloud&关闭iCloud备份
&&&&if&(self.config.shouldDisableiCloud)&{
&&&&&&&&[fileURL&setResourceValue:@YES&forKey:NSURLIsExcludedFromBackupKey&error:nil];
#pragma&mark&-&Query&and&Retrieve&Ops
//异步检查图片是否缓存在磁盘中
-&(void)diskImageExistsWithKey:(nullable&NSString&*)key&completion:(nullable&SDWebImageCheckCacheCompletionBlock)completionBlock&{
&&&&dispatch_async(_ioQueue,&^{
&&&&&&&&BOOL&exists&=&[_fileManager&fileExistsAtPath:[self&defaultCachePathForKey:key]];
&&&&&&&&//&fallback&because&of&https://github.com/rs/SDWebImage/pull/976&that&added&the&extension&to&the&disk&file&name
&&&&&&&&//&checking&the&key&with&and&without&the&extension
&&&&&&&&if&(!exists)&{
&&&&&&&&&&&&exists&=&[_fileManager&fileExistsAtPath:[self&defaultCachePathForKey:key].stringByDeletingPathExtension];
&&&&&&&&if&(completionBlock)&{
&&&&&&&&&&&&dispatch_async(dispatch_get_main_queue(),&^{
&&&&&&&&&&&&&&&&completionBlock(exists);
&&&&&&&&&&&&});
//在内存缓存中查询对应key的图片
-&(nullable&UIImage&*)imageFromMemoryCacheForKey:(nullable&NSString&*)key&{
&&&&return&[self.memCache&objectForKey:key];
//在磁盘缓存中查询对应key的图片&并且如果允许内存缓存则在内存中缓存
-&(nullable&UIImage&*)imageFromDiskCacheForKey:(nullable&NSString&*)key&{
&&&&UIImage&*diskImage&=&[self&diskImageForKey:key];
&&&&if&(diskImage&&&&self.config.shouldCacheImagesInMemory)&{
&&&&&&&&NSUInteger&cost&=&SDCacheCostForImage(diskImage);
&&&&&&&&[self.memCache&setObject:diskImage&forKey:key&cost:cost];
&&&&return&diskI
//在缓存中查询对应key的图片
-&(nullable&UIImage&*)imageFromCacheForKey:(nullable&NSString&*)key&{
&&&&//&First&check&the&in-memory&cache...
&&&&UIImage&*image&=&[self&imageFromMemoryCacheForKey:key];
&&&&if&(image)&{
&&&&&&&&return&
&&&&//&Second&check&the&disk&cache...
&&&&image&=&[self&imageFromDiskCacheForKey:key];
&&&&return&
//根据key在磁盘缓存中搜索图片data
-&(nullable&NSData&*)diskImageDataBySearchingAllPathsForKey:(nullable&NSString&*)key&{
&&&&NSString&*defaultPath&=&[self&defaultCachePathForKey:key];
&&&&NSData&*data&=&[NSData&dataWithContentsOfFile:defaultPath&options:self.config.diskCacheReadingOptions&error:nil];
&&&&if&(data)&{
&&&&&&&&return&
&&&&//&fallback&because&of&https://github.com/rs/SDWebImage/pull/976&that&added&the&extension&to&the&disk&file&name
&&&&//&checking&the&key&with&and&without&the&extension
&&&&data&=&[NSData&dataWithContentsOfFile:defaultPath.stringByDeletingPathExtension&options:self.config.diskCacheReadingOptions&error:nil];
&&&&if&(data)&{
&&&&&&&&return&
&&&&NSArray&*customPaths&=&[self.customPaths&copy];
&&&&for&(NSString&*path&in&customPaths)&{
&&&&&&&&NSString&*filePath&=&[self&cachePathForKey:key&inPath:path];
&&&&&&&&NSData&*imageData&=&[NSData&dataWithContentsOfFile:filePath&options:self.config.diskCacheReadingOptions&error:nil];
&&&&&&&&if&(imageData)&{
&&&&&&&&&&&&return&imageD
&&&&&&&&//&fallback&because&of&https://github.com/rs/SDWebImage/pull/976&that&added&the&extension&to&the&disk&file&name
&&&&&&&&//&checking&the&key&with&and&without&the&extension
&&&&&&&&imageData&=&[NSData&dataWithContentsOfFile:filePath.stringByDeletingPathExtension&options:self.config.diskCacheReadingOptions&error:nil];
&&&&&&&&if&(imageData)&{
&&&&&&&&&&&&return&imageD
&&&&return&
//根据key在磁盘缓存中搜索图片dimage
-&(nullable&UIImage&*)diskImageForKey:(nullable&NSString&*)key&{
&&&&NSData&*data&=&[self&diskImageDataBySearchingAllPathsForKey:key];
&&&&if&(data)&{
&&&&&&&&UIImage&*image&=&[[SDWebImageCodersManager&sharedInstance]&decodedImageWithData:data];
&&&&&&&&//根据图片的scale或图片中的图片组&重新计算返回一张新图片
&&&&&&&&image&=&[self&scaledImageForKey:key&image:image];
&&&&&&&&if&(self.config.shouldDecompressImages)&{
&&&&&&&&&&&&image&=&[[SDWebImageCodersManager&sharedInstance]&decompressedImageWithImage:image&data:&data&options:@{SDWebImageCoderScaleDownLargeImagesKey:&@(NO)}];
&&&&&&&&return&
&&&&}&else&{
&&&&&&&&return&
-&(nullable&UIImage&*)scaledImageForKey:(nullable&NSString&*)key&image:(nullable&UIImage&*)image&{
&&&&return&SDScaledImageForKey(key,&image);
//在缓存中查询对应key的图片信息&包含image,diskData以及缓存的类型
-&(nullable&NSOperation&*)queryCacheOperationForKey:(nullable&NSString&*)key&done:(nullable&SDCacheQueryCompletedBlock)doneBlock&{
&&&&if&(!key)&{
&&&&&&&&if&(doneBlock)&{
&&&&&&&&&&&&doneBlock(nil,&nil,&SDImageCacheTypeNone);
&&&&&&&&return&
&&&&//&First&check&the&in-memory&cache...如果内存缓存包含图片数据则回调结束
&&&&UIImage&*image&=&[self&imageFromMemoryCacheForKey:key];
&&&&if&(image)&{
&&&&&&&&NSData&*diskData&=&
&&&&&&&&if&(image.images)&{//imageData都存储在磁盘
&&&&&&&&&&&&diskData&=&[self&diskImageDataBySearchingAllPathsForKey:key];
&&&&&&&&if&(doneBlock)&{
&&&&&&&&&&&&doneBlock(image,&diskData,&SDImageCacheTypeMemory);
&&&&&&&&return&
&&&&NSOperation&*operation&=&[NSOperation&new];
&&&&dispatch_async(self.ioQueue,&^{
&&&&&&&&if&(operation.isCancelled)&{
&&&&&&&&&&&&//&do&not&call&the&completion&if&cancelled
&&&&&&&&&&&&
&&&&&&&&@autoreleasepool&{
&&&&&&&&&&&&//到这里说明已经来到磁盘缓存区获取&然后回调结束
&&&&&&&&&&&&NSData&*diskData&=&[self&diskImageDataBySearchingAllPathsForKey:key];
&&&&&&&&&&&&UIImage&*diskImage&=&[self&diskImageForKey:key];
&&&&&&&&&&&&if&(diskImage&&&&self.config.shouldCacheImagesInMemory)&{
&&&&&&&&&&&&&&&&NSUInteger&cost&=&SDCacheCostForImage(diskImage);
&&&&&&&&&&&&&&&&[self.memCache&setObject:diskImage&forKey:key&cost:cost];
&&&&&&&&&&&&}
&&&&&&&&&&&&if&(doneBlock)&{
&&&&&&&&&&&&&&&&dispatch_async(dispatch_get_main_queue(),&^{
&&&&&&&&&&&&&&&&&&&&doneBlock(diskImage,&diskData,&SDImageCacheTypeDisk);
&&&&&&&&&&&&&&&&});
&&&&&&&&&&&&}
&&&&return&
#pragma&mark&-&Remove&Ops
//删除缓存中指定key的图片
-&(void)removeImageForKey:(nullable&NSString&*)key&withCompletion:(nullable&SDWebImageNoParamsBlock)completion&{
&&&&[self&removeImageForKey:key&fromDisk:YES&withCompletion:completion];
//删除缓存中指定key的图片&磁盘是可选项
-&(void)removeImageForKey:(nullable&NSString&*)key&fromDisk:(BOOL)fromDisk&withCompletion:(nullable&SDWebImageNoParamsBlock)completion&{
&&&&if&(key&==&nil)&{
&&&&//如果内存也缓存了,则删除内存缓存
&&&&if&(self.config.shouldCacheImagesInMemory)&{
&&&&&&&&[self.memCache&removeObjectForKey:key];
&&&&if&(fromDisk)&{
&&&&&&&&dispatch_async(self.ioQueue,&^{
&&&&&&&&&&&&[_fileManager&removeItemAtPath:[self&defaultCachePathForKey:key]&error:nil];
&&&&&&&&&&&&
&&&&&&&&&&&&if&(completion)&{
&&&&&&&&&&&&&&&&dispatch_async(dispatch_get_main_queue(),&^{
&&&&&&&&&&&&&&&&&&&&completion();
&&&&&&&&&&&&&&&&});
&&&&&&&&&&&&}
&&&&&&&&});
&&&&}&else&if&(completion){
&&&&&&&&completion();
#&pragma&mark&-&Mem&Cache&settings
-&(void)setMaxMemoryCost:(NSUInteger)maxMemoryCost&{
&&&&self.memCache.totalCostLimit&=&maxMemoryC
-&(NSUInteger)maxMemoryCost&{
&&&&return&self.memCache.totalCostL
-&(NSUInteger)maxMemoryCountLimit&{
&&&&return&self.memCache.countL
-&(void)setMaxMemoryCountLimit:(NSUInteger)maxCountLimit&{
&&&&self.memCache.countLimit&=&maxCountL
#pragma&mark&-&Cache&clean&Ops
//清空所有的内存缓存
-&(void)clearMemory&{
&&&&[self.memCache&removeAllObjects];
//异步清除所有的磁盘缓存
-&(void)clearDiskOnCompletion:(nullable&SDWebImageNoParamsBlock)completion&{
&&&&dispatch_async(self.ioQueue,&^{
&&&&&&&&[_fileManager&removeItemAtPath:self.diskCachePath&error:nil];
&&&&&&&&[_fileManager&createDirectoryAtPath:self.diskCachePath
&&&&&&&&&&&&&&&&withIntermediateDirectories:YES
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&attributes:nil
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&error:NULL];
&&&&&&&&if&(completion)&{
&&&&&&&&&&&&dispatch_async(dispatch_get_main_queue(),&^{
&&&&&&&&&&&&&&&&completion();
&&&&&&&&&&&&});
-&(void)deleteOldFiles&{
&&&&[self&deleteOldFilesWithCompletionBlock:nil];
//异步清除所有失效的缓存图片-因为可以设定缓存时间,超过则失效
-&(void)deleteOldFilesWithCompletionBlock:(nullable&SDWebImageNoParamsBlock)completionBlock&{
&&&&dispatch_async(self.ioQueue,&^{
&&&&&&&&NSURL&*diskCacheURL&=&[NSURL&fileURLWithPath:self.diskCachePath&isDirectory:YES];
&&&&&&&&//resourceKeys数组包含遍历文件的属性,NSURLIsDirectoryKey判断遍历到的URL所指对象是否是目录,
&&&&&&&&//NSURLContentModificationDateKey判断遍历返回的URL所指项目的最后修改时间,NSURLTotalFileAllocatedSizeKey判断URL目录中所分配的空间大小
&&&&&&&&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];
&&&&&&&&//计算过期时间,默认1周以前的缓存文件是过期失效
&&&&&&&&NSDate&*expirationDate&=&[NSDate&dateWithTimeIntervalSinceNow:-self.config.maxCacheAge];
&&&&&&&&//保存遍历的文件url
&&&&&&&&NSMutableDictionary<NSURL&*,&NSDictionary&*>&*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.
&&&&&&&&//保存删除的文件url
&&&&&&&&NSMutableArray&*urlsToDelete&=&[[NSMutableArray&alloc]&init];
&&&&&&&&//遍历目录枚举器,目的1删除过期文件&2纪录文件大小,以便于之后删除使用
&&&&&&&&for&(NSURL&*fileURL&in&fileEnumerator)&{
&&&&&&&&&&&&NSError&*
&&&&&&&&&&&&//获取指定url对应文件的指定三种属性的key和value
&&&&&&&&&&&&NSDictionary&*resourceValues&=&[fileURL&resourceValuesForKeys:resourceKeys&error:&error];
&&&&&&&&&&&&//&Skip&directories&and&errors.
&&&&&&&&&&&&//如果是文件夹则返回
&&&&&&&&&&&&if&(error&||&!resourceValues&||&[resourceValues[NSURLIsDirectoryKey]&boolValue])&{
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&}
&&&&&&&&&&&&//&Remove&files&that&are&older&than&the&expiration&
&&&&&&&&&&&&//获取指定url文件对应的修改日期
&&&&&&&&&&&&NSDate&*modificationDate&=&resourceValues[NSURLContentModificationDateKey];
&&&&&&&&&&&&//如果修改日期大于指定日期,则加入要移除的数组里
&&&&&&&&&&&&if&([[modificationDate&laterDate:expirationDate]&isEqualToDate:expirationDate])&{
&&&&&&&&&&&&&&&&[urlsToDelete&addObject:fileURL];
&&&&&&&&&&&&&&&&
&&&&&&&&&&&&}
&&&&&&&&&&&&//&Store&a&reference&to&this&file&and&account&for&its&total&size.
&&&&&&&&&&&&//获取指定的url对应的文件的大小,并且把url与对应大小存入一个字典中
&&&&&&&&&&&&NSNumber&*totalAllocatedSize&=&resourceValues[NSURLTotalFileAllocatedSizeKey];
&&&&&&&&&&&&currentCacheSize&+=&totalAllocatedSize.unsignedIntegerV
&&&&&&&&&&&&cacheFiles[fileURL]&=&resourceV
&&&&&&&&//删除所有最后修改日期大于指定日期的所有文件
&&&&&&&&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.
&&&&&&&&//如果当前缓存的大小超过了默认大小,则按照日期删除,直到缓存大小&0&&&&currentCacheSize&>&self.config.maxCacheSize)&{
&&&&&&&&&&&&//&Target&half&of&our&maximum&cache&size&for&this&cleanup&pass.
&&&&&&&&&&&&const&NSUInteger&desiredCacheSize&=&self.config.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.unsignedIntegerV
&&&&&&&&&&&&&&&&&&&&if&(currentCacheSize&<&desiredCacheSize)&{
&&&&&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}
&&&&&&&&//在主线程中回调
&&&&&&&&if&(completionBlock)&{
&&&&&&&&&&&&dispatch_async(dispatch_get_main_queue(),&^{
&&&&&&&&&&&&&&&&completionBlock();
&&&&&&&&&&&&});
#if&SD_UIKIT
//应用进入后台的时候,调用这个方法 然后清除过期图片
-&(void)backgroundDeleteOldFiles&{
&&&&Class&UIApplicationClass&=&NSClassFromString(@"UIApplication");
&&&&if(!UIApplicationClass&||&![UIApplicationClass&respondsToSelector:@selector(sharedApplication)])&{
&&&&UIApplication&*application&=&[UIApplication&performSelector:@selector(sharedApplication)];
&&&&__block&UIBackgroundTaskIdentifier&bgTask&=&[application&beginBackgroundTaskWithExpirationHandler:^{
&&&&&&&&//&Clean&up&any&unfinished&task&business&by&marking&where&you
&&&&&&&&//&stopped&or&ending&the&task&outright.
&&&&&&&&[application&endBackgroundTask:bgTask];
&&&&&&&&bgTask&=&UIBackgroundTaskI
&&&&//&Start&the&long-running&task&and&return&immediately.
&&&&[self&deleteOldFilesWithCompletionBlock:^{
&&&&&&&&[application&endBackgroundTask:bgTask];
&&&&&&&&bgTask&=&UIBackgroundTaskI
#pragma&mark&-&Cache&Info
//得到磁盘缓存的大小size
-&(NSUInteger)getSize&{
&&&&__block&NSUInteger&size&=&0;
&&&&dispatch_sync(self.ioQueue,&^{
&&&&&&&&NSDirectoryEnumerator&*fileEnumerator&=&[_fileManager&enumeratorAtPath:self.diskCachePath];
&&&&&&&&for&(NSString&*fileName&in&fileEnumerator)&{
&&&&&&&&&&&&NSString&*filePath&=&[self.diskCachePath&stringByAppendingPathComponent:fileName];
&&&&&&&&&&&&NSDictionary&*attrs&=&[[NSFileManager&defaultManager]&attributesOfItemAtPath:filePath&error:nil];
&&&&&&&&&&&&size&+=&[attrs&fileSize];
&&&&return&
//得到在磁盘缓存中图片的数量
-&(NSUInteger)getDiskCount&{
&&&&__block&NSUInteger&count&=&0;
&&&&dispatch_sync(self.ioQueue,&^{
&&&&&&&&NSDirectoryEnumerator&*fileEnumerator&=&[_fileManager&enumeratorAtPath:self.diskCachePath];
&&&&&&&&count&=&fileEnumerator.allObjects.
&&&&return&
//异步计算磁盘缓存的大小
-&(void)calculateSizeWithCompletionBlock:(nullable&SDWebImageCalculateSizeBlock)completionBlock&{
&&&&NSURL&*diskCacheURL&=&[NSURL&fileURLWithPath:self.diskCachePath&isDirectory:YES];
&&&&dispatch_async(self.ioQueue,&^{
&&&&&&&&NSUInteger&fileCount&=&0;
&&&&&&&&NSUInteger&totalSize&=&0;
&&&&&&&&NSDirectoryEnumerator&*fileEnumerator&=&[_fileManager&enumeratorAtURL:diskCacheURL
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&includingPropertiesForKeys:@[NSFileSize]
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&options:NSDirectoryEnumerationSkipsHiddenFiles
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&errorHandler:NULL];
&&&&&&&&for&(NSURL&*fileURL&in&fileEnumerator)&{
&&&&&&&&&&&&NSNumber&*fileS
&&&&&&&&&&&&[fileURL&getResourceValue:&fileSize&forKey:NSURLFileSizeKey&error:NULL];
&&&&&&&&&&&&totalSize&+=&fileSize.unsignedIntegerV
&&&&&&&&&&&&fileCount&+=&1;
&&&&&&&&if&(completionBlock)&{
&&&&&&&&&&&&dispatch_async(dispatch_get_main_queue(),&^{
&&&&&&&&&&&&&&&&completionBlock(fileCount,&totalSize);
&&&&&&&&&&&&});
@end下图是在SDWebImageManager中调用SDCacheImage查找缓存的关键流程图:此篇文章讲解的是UIImageView+WebCache/UIView+WebCache,SDWebImageManager和SDImageCache三个关键类,有许多很值得我们去学习的点,例如:1.善用接口分离原则-设计更好的对外调用API。2.适当做异常处理机制-这些异常处理可以避免消耗不必要的资源或者异常发生。例如SDWebImageManager中if&(!url)&{&return&@"";}
&if&([url&isKindOfClass:NSString.class])&{
&&&&&url&=&[NSURL&URLWithString:(NSString&*)url];
&}3.增加互斥锁-起到线程的保护作用。@synchronized&(self.failedURLs)&{
&&&&&isFailedUrl&=&[self.failedURLs&containsObject:url];
&}4.多利用inline函数-提高程序的执行效率。FOUNDATION_STATIC_INLINE&NSUInteger&SDCacheCostForImage(UIImage&*image)&{
#if&SD_MAC
&&&&return&image.size.height&*&image.size.
#elif&SD_UIKIT&||&SD_WATCH
&&&&return&image.size.height&*&image.size.width&*&image.scale&*&image.
}5.巧妙利用封装思想和分层概念,写出更加平台化的组件-例如我们日常使用SD大多都是利用UIImageView+WebCache的API, 其实SDWebImageManager是完全可以抽出来单独使用,不会因为跳过了UIImageView+WebCache没了依赖而无法使用。如果项目是需要多人跨多部门合作,如果他人或其他部门需要调用你写的一个很牛逼的功能,那这个平台化就很重要了。SDWebImage还有很多值得我们借鉴和学习的地方,需要大家细细研读。下一篇会讲解SDWebImageDownloader和SDWebImageDownloaderOperation。作者:厦大
微信扫一扫
订阅每日移动开发及APP推广热点资讯公众号:CocoaChina
您还没有登录!请或
点击量11036点击量6261点击量4820点击量4792点击量3578点击量3262点击量3109点击量2918点击量2817
&2016 Chukong Technologies,Inc.
京公网安备89

我要回帖

更多关于 大姨妈会传染吗 的文章

 

随机推荐