SDWebImage源码解读之SDWebImageDownloader.docx
《SDWebImage源码解读之SDWebImageDownloader.docx》由会员分享,可在线阅读,更多相关《SDWebImage源码解读之SDWebImageDownloader.docx(24页珍藏版)》请在冰豆网上搜索。
SDWebImage源码解读之SDWebImageDownloader
SDWebImage源码解读之SDWebImageDownloader
SDWebImageDownloader这个类非常简单,作者的设计思路也很清晰,但是我想在这说点题外话。
如果有人问你:
你怎么看待编程这件事?
你怎么回答。
这个问题是我在看这个类的时候,忽然出现在我脑子中的。
我突然意识到,其实不管是函数还是属性,他们都是数据。
我们编写的所有程序都是在处理数据。
函数本身也是一种特殊的数据。
真正难的是生产数据的这一过程。
举个例子,给你一堆菜籽,要求生产出油来。
怎么办?
我们首先为这个任务设计一个函数:
-(油)用菜籽生产油(菜籽);
这就是我们最外层的函数,也应该是我们最开始想到的函数。
然后经过我们的研究发现,这个生产过程很复杂,必须分工合作才能实现。
于是我们把这个任务分割为好几个小任务:
1.-(干净的菜籽)取出杂质(菜籽);
2.-(炒熟的菜籽)把菜籽炒一下(干净的菜籽);
3.-(蒸了的菜籽)把菜籽蒸一下(炒熟的菜籽);
4.-(捆好的菜籽)把菜籽包捆成一块(蒸了的菜籽);
5.-(油)撞击菜籽包(捆好的菜籽);
大家有没有发现,整个榨油的过程就是对数据的处理。
这一点其实很重要。
如果没有把-(油)用菜籽生产油(菜籽);这一任务进行拆分,我们就会写出复杂无比的函数。
那么就有人要问了,只要实现这个功能就行了呗。
其实这往往是写不出好代码的原因。
整个任务的设计应该是事先就设计好的。
任务被分割成更小更简单的部分,然后再去实现这些最小的任务,不应该是编写边分割任务,往往临时分割的任务(也算是私有函数吧)没有最正确的界限。
有了上边合理的分工之后呢,我们就可以进行任务安排了。
我们回到现实开发中来。
上边5个子任务的难度是不同的。
有的人可能基础比较差,那么让他去干筛菜籽这种体力活,应该没问题。
那些炒或者蒸的子任务是要掌握火候的,也就是说有点技术含量。
那么就交给能胜任这项工作的人去做。
所有的这一切,我们只要事先定义好各自的生产结果就行了,完全不影响每个程序的执行。
怎么样?
大家体会到这种编程设计的好处了吗?
我还可以进行合并,把炒和煮合成一个小组,完全可行啊。
好了这方面的思考就说这么多吧。
如果我想买煮熟了的菜籽,是不是也很简单?
有的人用原始的撞击菜籽包榨油,有的人却用最先进的仪器榨油,这就是编程技术和知识深度的区别啊。
SDWebImageDownloaderOptions
言归正传,当我们需要给某个功能添加Options的时候,一般使用枚举来实现。
我们先看看支持的选项:
typedefNS_OPTIONS(NSUInteger,SDWebImageDownloaderOptions){
SDWebImageDownloaderLowPriority=1<<0,
SDWebImageDownloaderProgressiveDownload=1<<1,//带有进度
SDWebImageDownloaderUseNSURLCache=1<<2,//使用URLCache
SDWebImageDownloaderIgnoreCachedResponse=1<<3,//不缓存响应
SDWebImageDownloaderContinueInBackground=1<<4,//支持后台下载
SDWebImageDownloaderHandleCookies=1<<5,//使用Cookies
SDWebImageDownloaderAllowInvalidSSLCertificates=1<<6,//允许验证SSL
SDWebImageDownloaderHighPriority=1<<7,//高权限
SDWebImageDownloaderScaleDownLargeImages=1<<8,//裁剪大图片
};
这里提供了这么几种不同的选项,大家可以根据自己的需求选个合适的选项。
这里作者使用了掩码。
比如说,1<<1,表示把1左移一位,我们把1携程二进制为:
00000001,那么左移一位后就是:
00000010转成10进制后就是2,也就是说左移一位表示在原来的值上乘以2。
再举个例子,当判断self.option是否是SDWebImageDownloaderIgnoreCachedResponse选项时,应该这么判断:
self.option&SDWebImageDownloaderIgnoreCachedResponse
SDWebImageDownloaderExecutionOrder
SDWebImageDownloaderExecutionOrder定义了数据被调用的顺序。
按照一般的想法。
下载应该按照数据放入队列的顺序依次进行,但也支持后进先出这种方式。
一个下载管理器应该这样管理下载,肯定有一个下载列表,我们可以假定这个列表保存在一个数组中,正常情况下我们应该每次取出数组中第1个元素来下载,这就是FIFO(先进先出)。
那么我们要改为LIFO(后进先出),应该是针对某一个下载的,不应该是把取出数据的顺序改为从数组的最后一个元素取出。
typedefNS_ENUM(NSInteger,SDWebImageDownloaderExecutionOrder){
/**
*Defaultvalue.Alldownloadoperationswillexecuteinqueuestyle(first-in-first-out).
*/
SDWebImageDownloaderFIFOExecutionOrder,
/**
*Alldownloadoperationswillexecuteinstackstyle(last-in-first-out).
*/
SDWebImageDownloaderLIFOExecutionOrder
};
但是我有一个疑问,如果我只是想暂停某一个下载,我该怎么办呢?
如果直接取消,那么点击继续的时候,要重新开启下载,肯定影响体验。
这一点跟使用数组管理任务有点不同。
辅助部分
externNSString*_NonnullconstSDWebImageDownloadStartNotification;
externNSString*_NonnullconstSDWebImageDownloadStopNotification;
通过extern,我们就能够使用其他文件的代码。
typedefvoid(^SDWebImageDownloaderProgressBlock)(NSIntegerreceivedSize,NSIntegerexpectedSize,NSURL*_NullabletargetURL);
typedefvoid(^SDWebImageDownloaderCompletedBlock)(UIImage*_Nullableimage,NSData*_Nullabledata,NSError*_Nullableerror,BOOLfinished);
命名Block
typedefNSDictionarySDHTTPHeadersDictionary;
typedefNSMutableDictionarySDHTTPHeadersMutableDictionary;
命名字典
typedefSDHTTPHeadersDictionary*_Nullable(^SDWebImageDownloaderHeadersFilterBlock)(NSURL*_Nullableurl,SDHTTPHeadersDictionary*_Nullableheaders);
这个block允许我们自定义请求头,通过Block传值,有一定的优点,我们可以拿到一些参数,然后在加工成我们需要的数据,最后返回。
SDWebImageDownloadToken
SDWebImageDownloadToken作为每一个下载的唯一身份标识,SDWebImageDownloader和我们平时开发中的下载还是又不一样的地方的,它弱化了下载过程,比较强调的是下载结果。
不支持断点下载(当然这可能没有必要)。
如果我们需要设计一个自己的下载管理者,我们就应该设计一个类似SDWebImageDownloadToken这样的下载对象封装类,我需要的所有信息,都能在这个对象中获取,大家如果有兴趣,可以看看MCDownloadManager.
/**
*Atokenassociatedwitheachdownload.Canbeusedtocanceladownload
*/
@interfaceSDWebImageDownloadToken:
NSObject
@property(nonatomic,strong,nullable)NSURL*url;
@property(nonatomic,strong,nullable)iddownloadOperationCancelToken;
@end
SDWebImageDownloader.h
/**
*Decompressingimagesthataredownloadedandcachedcanimproveperformancebutcanconsumelotofmemory.
*DefaultstoYES.SetthistoNOifyouareexperiencingacrashduetoexcessivememoryconsumption.
*/
@property(assign,nonatomic)BOOLshouldDecompressImages;
/**
*Themaximumnumberofconcurrentdownloads
*/
@property(assign,nonatomic)NSIntegermaxConcurrentDownloads;
/**
*Showsthecurrentamountofdownloadsthatstillneedtobedownloaded
*/
@property(readonly,nonatomic)NSUIntegercurrentDownloadCount;
/**
*Thetimeoutvalue(inseconds)forthedownloadoperation.Default:
15.0.
*/
@property(assign,nonatomic)NSTimeIntervaldownloadTimeout;
/**
*Changesdownloadoperationsexecutionorder.Defaultvalueis`SDWebImageDownloaderFIFOExecutionOrder`.
*/
@property(assign,nonatomic)SDWebImageDownloaderExecutionOrderexecutionOrder;
/**
*Singletonmethod,returnsthesharedinstance
*
*@returnglobalsharedinstanceofdownloaderclass
*/
+(nonnullinstancetype)sharedDownloader;
/**
*SetthedefaultURLcredentialtobesetforrequestoperations.
*/
@property(strong,nonatomic,nullable)NSURLCredential*urlCredential;
/**
*Setusername
*/
@property(strong,nonatomic,nullable)NSString*username;
/**
*Setpassword
*/
@property(strong,nonatomic,nullable)NSString*password;
/**
*SetfiltertopickheadersfordownloadingimageHTTPrequest.
*
*Thisblockwillbeinvokedforeachdownloadingimagerequest,returned
*NSDictionarywillbeusedasheadersincorrespondingHTTPrequest.
*/
@property(nonatomic,copy,nullable)SDWebImageDownloaderHeadersFilterBlockheadersFilter;
初始化方法
/**
*Createsaninstanceofadownloaderwithspecifiedsessionconfiguration.
**Note*:
`timeoutIntervalForRequest`isgoingtobeoverwritten.
*@returnnewinstanceofdownloaderclass
*/
-(nonnullinstancetype)initWithSessionConfiguration:
(nullableNSURLSessionConfiguration*)sessionConfigurationNS_DESIGNATED_INITIALIZER;
我们都知道:
一个NSURLSession会话,使用NSURLSessionConfiguration进行配置,利用这一点,我们在设计自己的网络框架的时候,可以参考NSURLSessionConfiguration。
暴露少量的属性来配置网络请求,后期维护起来也比较容易。
使用NS_DESIGNATED_INITIALIZER强调该方法是建议的初始化方法。
其他的方法
/**
*SetavalueforaHTTPheadertobeappendedtoeachdownloadHTTPrequest.
*
*@paramvalueThevaluefortheheaderfield.Use`nil`valuetoremovetheheader.
*@paramfieldThenameoftheheaderfieldtoset.
*/
-(void)setValue:
(nullableNSString*)valueforHTTPHeaderField:
(nullableNSString*)field;
/**
*ReturnsthevalueofthespecifiedHTTPheaderfield.
*
*@returnThevalueassociatedwiththeheaderfieldfield,or`nil`ifthereisnocorrespondingheaderfield.
*/
-(nullableNSString*)valueForHTTPHeaderField:
(nullableNSString*)field;
/**
*Setsasubclassof`SDWebImageDownloaderOperation`asthedefault
*`NSOperation`tobeusedeachtimeSDWebImageconstructsarequest
*operationtodownloadanimage.
*
*@paramoperationClassThesubclassof`SDWebImageDownloaderOperation`toset
*asdefault.Passing`nil`willrevertto`SDWebImageDownloaderOperation`.
*/
-(void)setOperationClass:
(nullableClass)operationClass;
/**
*CreatesaSDWebImageDownloaderasyncdownloaderinstancewithagivenURL
*
*Thedelegatewillbeinformedwhentheimageisfinishdownloadedoranerrorhashappen.
*
*@seeSDWebImageDownloaderDelegate
*
*@paramurlTheURLtotheimagetodownload
*@paramoptionsTheoptionstobeusedforthisdownload
*@paramprogressBlockAblockcalledrepeatedlywhiletheimageisdownloading
*@notetheprogressblockisexecutedonabackgroundqueue
*@paramcompletedBlockAblockcalledoncethedownloadiscompleted.
*Ifthedownloadsucceeded,theimageparameterisset,incaseoferror,
*errorparameterissetwiththeerror.ThelastparameterisalwaysYES
*ifSDWebImageDownloaderProgressiveDownloadisn'tuse.Withthe
*SDWebImageDownloaderProgressiveDownloadoption,thisblockiscalled
*repeatedlywiththepartialimageobjectandthefinishedargumentsettoNO
*beforetobecalledalasttimewiththefullimageandfinishedargument
*settoYES.Incaseoferror,thefinishedargumentisalwaysYES.
*
*@returnAtoken(SDWebImageDownloadToken)thatcanbepassedto-cancel:
tocancelthisoperation
*/
-(nullableSDWebImageDownloadToken*)downloadImageWithURL:
(nullableNSURL*)url
options:
(SDWebImageDownloaderOptions)options
progress:
(nullableSDWebImageDownloaderProgressBlock)progressBlock
completed:
(nullableSDWebImageDownloaderCompletedBlock)completedBlock;
/**
*Cancelsadownloadthatwaspreviouslyqueuedusing-downloadImageWithURL:
options:
progress:
completed:
*
*@paramtokenThetokenreceivedfrom-downloadImageWithURL:
options:
progress:
completed:
thatshouldbecanceled.
*/
-(void)cancel:
(nullableSDWebImageDownloadToken*)token;
/**
*Setsthedownloadqueuesuspensionstate
*/
-(void)setSuspended:
(BOOL)suspended;
/**
*Cancelsalldownloadoperationsinthequeue
*/
-(void)cancelAllDownloads;
SDWebImageDownloader.m
@property(strong,nonatomic,nonnull)NSOperationQueue*downloadQueue;
@property(weak,nonatomic,nullable)NSOperation*lastAddedOperation;
@property(assign,nonatomic,nullable)ClassoperationClass;
@property(strong,nonatomic,nonnull)NSMutableDictionary*URLOperations;
@property(strong,nonatomic,nullable)SDHTTPHeadersMutableDictionary*HTTPHeaders;
//Thisqueueisusedtoserializethehandlingofthenetworkresponsesofallthedownloadoperationinasinglequeue
@property(SDDispatchQueueSetterSementics,nonatomic,nullable)dispatch_queue_tbarrierQueue;
//Thesessioninwhichdatataskswillrun
@property(strong,nonatomic)NS