SDWebImage之SDWebImageDownloader
SDWebImageDownloader
完成了对网络图片的异步下载工作,准确说这个类是一个文件下载的工具类,真正的网络请求是在继承于NSOperation
的SDWebImageDownloaderOperation
类实现的。SDWebImageDownloader的主要任务是下载相关配置项的管理,包括下载队列的先后顺序、最大下载任务数量控制、下载队列中的任务创建、取消、暂停等任务管理,以及其他 HTTPS 和 HTTP Header的设置。
1.SDWebImageDownloaderOptions
typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
//默认模式
SDWebImageDownloaderLowPriority = << ,
//本模式在返回进度Block的同时,同事返回completedBlock,里面的UIImage就是当前下载时的图片,可以实现将图片一点点显示出来的功能
SDWebImageDownloaderProgressiveDownload = << ,
//默认情况下,http请求阻止使用NSURLCache对象。如果设置了这个标记,则NSURLCache会被http请求使用。
SDWebImageDownloaderUseNSURLCache = << ,
//如果image/imageData是从NSURLCache返回的,则completion这个回调会返回nil
SDWebImageDownloaderIgnoreCachedResponse = << ,
//如果app进入后台模式,是否继续下载,这个是通过在后台申请时间来完成这个操作。如果指定的时间范围内没有完成,则直接取消下载。
SDWebImageDownloaderContinueInBackground = << ,
//处理缓存在`NSHTTPCookieStore`对象里面的cookie,通过设置`NSMutableURLRequest.HTTPShouldHandleCookies = YES`来实现的。
SDWebImageDownloaderHandleCookies = << ,
//允许非信任的SSL证书请求。在测试的时候很有用,但是正式环境要小心使用。
SDWebImageDownloaderAllowInvalidSSLCertificates = << ,
//默认情况下,图片加载的顺序是根据加入队列的顺序加载的。但是这个标记会把任务加入队列的最前面。
SDWebImageDownloaderHighPriority = << ,
//默认情况下,图片会按照它的原始大小来解码显示。这个属性会根据设备的内存限制调整图片的尺寸到合适的大小。如果`SDWebImageProgressiveDownload`标记被设置了,则这个flag不起作用。
SDWebImageDownloaderScaleDownLargeImages = << ,
};
在这里留意一下,选项使用的是掩码的方式。例如“1 << 1”,表示把1左移一位。我们把1用二进制表示为:00000001,那么左移一位后就是:00000010 转成10进制后就是2,也就是说左移一位表示在原来的值上乘以2。
这样,在使用的时候,当判断options是否是SDWebImageDownloaderIgnoreCachedResponse选项时,应该这样来判断:
self.option & SDWebImageDownloaderIgnoreCachedResponse
2.SDWebImageDownloaderExecutionOrder
typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
/**
* Default value. All download operations will execute in queue style (first-in-first-out).
*/
SDWebImageDownloaderFIFOExecutionOrder, /**
* All download operations will execute in stack style (last-in-first-out).
*/
SDWebImageDownloaderLIFOExecutionOrder
};
SDWebImageDownloaderExecutionOrder
定义了数据被调用的顺序。按照一般的想法,下载应该按照数据放入队列的顺序依次进行,在SDWebImage中也支持后进先出这种方式。
3.变量定义
extern NSString * _Nonnull const SDWebImageDownloadStartNotification;
extern NSString * _Nonnull const SDWebImageDownloadStopNotification; typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL); typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished); typedef NSDictionary<NSString *, NSString *> SDHTTPHeadersDictionary;
typedef NSMutableDictionary<NSString *, NSString *> SDHTTPHeadersMutableDictionary;
//自定义请求头,通过Block传值,可以拿到一些参数,然后加工成我们需要的数据,最后返回
typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterBlock)(NSURL * _Nullable url, SDHTTPHeadersDictionary * _Nullable headers);
4.SDWebImageDownloadToken
@interface SDWebImageDownloadToken : NSObject @property (nonatomic, strong, nullable) NSURL *url;
@property (nonatomic, strong, nullable) id downloadOperationCancelToken; @end
SDWebImageDownloadToken为
每一个下载任务的唯一身份标识,SDWebImageDownloader
和我们平时开发中的下载有一些不同,它弱化了下载过程,比较强调的是下载结果,不支持断点下载。
5.相关属性
//.h文件
@property (assign, nonatomic) BOOL shouldDecompressImages; //!<当图片下载完成以后,解码图片。如果因为过多的内存消耗导致一个奔溃,可以把这个属性设置为NO。 @property (assign, nonatomic) NSInteger maxConcurrentDownloads; //!<最大并行下载的数量 @property (readonly, nonatomic) NSUInteger currentDownloadCount; //!<当前并行下载数量 @property (assign, nonatomic) NSTimeInterval downloadTimeout; //!<下载超时时间设置 @property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder; //!<改变下载operation的执行顺序,默认是FIFO。 @property (strong, nonatomic, nullable) NSURLCredential *urlCredential; //!<为图片加载request设置一个SSL证书对象 @property (strong, nonatomic, nullable) NSString *username; //!<Basic认证请求设置用户名 @property (strong, nonatomic, nullable) NSString *password; //!<Basic认证请求设置密码 @property (nonatomic, copy, nullable) SDWebImageDownloaderHeadersFilterBlock headersFilter; //!<为http请求设置header。每一request执行的时候,这个Block都会被执行。用于向http请求添加请求域 //.m文件
@property (strong, nonatomic, nonnull) NSOperationQueue *downloadQueue; //!<所有的下载图片的Operation都加入NSOperationQueue中
@property (weak, nonatomic, nullable) NSOperation *lastAddedOperation; //!<最后一个添加的Operation
@property (assign, nonatomic, nullable) Class operationClass; //!<自定义的NSOperation子类
@property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations; //!<用于记录url和它对应的SDWebImageDownloaderOperation对象
@property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders; //!<请求头域字典
// This queue is used to serialize the handling of the network responses of all the download operation in a single queue
@property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t barrierQueue; // The session in which data tasks will run
@property (strong, nonatomic) NSURLSession *session; //!<通过这个`NSURLSession`创建请求
6.initialize方法
+ (void)initialize {
// Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator )
// To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import
if (NSClassFromString(@"SDNetworkActivityIndicator")) { #pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")];
#pragma clang diagnostic pop // Remove observer in case it was previously added.
[[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStartNotification object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStopNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:activityIndicator
selector:NSSelectorFromString(@"startActivity")
name:SDWebImageDownloadStartNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:activityIndicator
selector:NSSelectorFromString(@"stopActivity")
name:SDWebImageDownloadStopNotification object:nil];
}
}
该方法是为了给图片下载绑定一个SDNetworkActivityIndicator
,只有当这个SDNetworkActivityIndicator
文件存在的情况下才会执行,目的就是当下载图片时,状态栏会转小菊花。
initialize
和 load
这两个方法比较特殊,我们通过下表来看看它们之间的区别:
+(void)load | +(void)initialize | |
执行时机 | 在程序运行后立即执行(在main函数执行之前) | 在类的方法第一次被调时执行 |
若自身未定义,是否沿用父类的方法? | 否 | 是 |
分类中的定义 | 全都执行,但后于类中的方法 | 覆盖类中的方法,只执行一个 |
7.初始化
/**
单例方法 @return 返回SDWebImageDownloader对象
*/
+ (nonnull instancetype)sharedDownloader {
static dispatch_once_t once;
static id instance;
dispatch_once(&once, ^{
instance = [self new];
});
return instance;
} - (nonnull instancetype)init {
return [self initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
} /**
初始化一个请求对象 @param sessionConfiguration NSURLSessionTask初始化配置
@return 返回SDWebImageDownloader对象
*/
- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration {
if ((self = [super init])) {
_operationClass = [SDWebImageDownloaderOperation class];
_shouldDecompressImages = YES;
_executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
_downloadQueue = [NSOperationQueue new];
_downloadQueue.maxConcurrentOperationCount = ;
_downloadQueue.name = @"com.hackemist.SDWebImageDownloader";
_URLOperations = [NSMutableDictionary new];
#ifdef SD_WEBP
_HTTPHeaders = [@{@"Accept": @"image/webp,image/*;q=0.8"} mutableCopy];
#else
_HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy];
#endif
_barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
_downloadTimeout = 15.0; sessionConfiguration.timeoutIntervalForRequest = _downloadTimeout; /**
* Create the session for this task
* We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
* method calls and completion handler calls.
*/
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
delegate:self
delegateQueue:nil];
}
return self;
}
在上面的初始化方法中,我们可以看到,默认最大支持的并发数为6个,也就是说可以同时下载6张图片。
我们看看image/webp,image/*;q=0.8
是什么意思,image/webp
是webp格式的图片,q=0.8
指的是权重系数为0.8,q的取值范围是0 - 1, 默认值为1,q作用于它前边分号;
前边的内容。在这里,image/webp,image/*;q=0.8
表示优先接受image/webp
,其次接受image/*
的图片。
8.Set&&Get
/**
设置请求头域 @param value 请求头域值
@param field 请求头域名
*/
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field {
if (value) {
self.HTTPHeaders[field] = value;
}
else {
[self.HTTPHeaders removeObjectForKey:field];
}
} /**
获取请求头域的值 @param field 请求头域名
@return 请求头域值
*/
- (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field {
return self.HTTPHeaders[field];
} /**
设置最大并发数 @param maxConcurrentDownloads 最大并发数
*/
- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads {
_downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads;
} /**
获取当前并行下载数量 @return 当前并行下载数量
*/
- (NSUInteger)currentDownloadCount {
return _downloadQueue.operationCount;
} /**
获取最大并发数 @return 最大并发数
*/
- (NSInteger)maxConcurrentDownloads {
return _downloadQueue.maxConcurrentOperationCount;
} /**
设置一个`SDWebImageDownloaderOperation`的子类作为`NSOperation`来构建request来下载一张图片 @param operationClass 指定的子类
*/
- (void)setOperationClass:(nullable Class)operationClass {
if (operationClass && [operationClass isSubclassOfClass:[NSOperation class]] && [operationClass conformsToProtocol:@protocol(SDWebImageDownloaderOperationInterface)]) {
_operationClass = operationClass;
} else {
_operationClass = [SDWebImageDownloaderOperation class];
}
}
8.下载
/**
下载图片 @param url url
@param options 加载选项
@param progressBlock 进度progress
@param completedBlock 完成回调
@return 返回一个SDWebImageDownloadToken,用于关联一个请求
*/
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
__weak SDWebImageDownloader *wself = self; return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
__strong __typeof (wself) sself = wself;
//设置超时时间
NSTimeInterval timeoutInterval = sself.downloadTimeout;
if (timeoutInterval == 0.0) {
timeoutInterval = 15.0;
} // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
//为了避免可能存在的NSURLCache和SDImageCache同时缓存,我们默认不允许image对象的NSURLCache对象。具体缓存策略参考http://www.jianshu.com/p/855c2c6e761f
NSURLRequestCachePolicy cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
if (options & SDWebImageDownloaderUseNSURLCache) {
if (options & SDWebImageDownloaderIgnoreCachedResponse) {
cachePolicy = NSURLRequestReturnCacheDataDontLoad;
} else {
cachePolicy = NSURLRequestUseProtocolCachePolicy;
}
}
//创建request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:cachePolicy timeoutInterval:timeoutInterval];
//使用cookies
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
//使用管道
request.HTTPShouldUsePipelining = YES;
//添加自定义请求头
if (sself.headersFilter) {
request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = sself.HTTPHeaders;
}
//初始化一个自定义NSOperation对象
SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
//是否解码返回的图片
operation.shouldDecompressImages = sself.shouldDecompressImages;
//指定验证信息
if (sself.urlCredential) {
//SSL验证
operation.credential = sself.urlCredential;
} else if (sself.username && sself.password) { //Basic验证
operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
}
//指定优先级
if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) {
operation.queuePriority = NSOperationQueuePriorityLow;
}
//把operation添加进入NSOperationQueue中,当operation添加到downloadQueue,会触发相应的start方法,开始下载。
[sself.downloadQueue addOperation:operation];
//如果是LIFO这种模式,则需要手动指定operation之间的依赖关系
if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
//如果是LIFO,则让前面的operation依赖于最新添加的operation
[sself.lastAddedOperation addDependency:operation];
sself.lastAddedOperation = operation;
} return operation;
}];
} /**
移除一个图片的下载操作 @param token 通过token来确定操作
*/
- (void)cancel:(nullable SDWebImageDownloadToken *)token {
dispatch_barrier_async(self.barrierQueue, ^{
SDWebImageDownloaderOperation *operation = self.URLOperations[token.url];
BOOL canceled = [operation cancel:token.downloadOperationCancelToken];
if (canceled) {
[self.URLOperations removeObjectForKey:token.url];
}
});
} /**
给下载过程添加进度 @param progressBlock 进度Block
@param completedBlock 完成Block
@param url url地址
@param createCallback nil
@return 返回SDWebImageDownloadToken,方便后面取消
*/
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
forURL:(nullable NSURL *)url
createCallback:(SDWebImageDownloaderOperation *(^)())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.
//如果URL为空,则执行completedBlock回调,并直接返回
if (url == nil) {
if (completedBlock != nil) {
completedBlock(nil, nil, nil, NO);
}
return nil;
} __block SDWebImageDownloadToken *token = nil; dispatch_barrier_sync(self.barrierQueue, ^{
//看是否当前url是否有对应的Operation图片加载对象
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
//如果没有,则直接创建一个
if (!operation) {
//创建一个operation,并且添加到URLOperation中
operation = createCallback();
self.URLOperations[url] = operation; __weak SDWebImageDownloaderOperation *woperation = operation;
//设置operation操作完成以后的回调
operation.completionBlock = ^{
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
};
}
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock]; token = [SDWebImageDownloadToken new];
token.url = url;
token.downloadOperationCancelToken = downloadOperationCancelToken;
}); return token;
} /**
全部暂停/开始 @param suspended YES/NO
*/
- (void)setSuspended:(BOOL)suspended {
(self.downloadQueue).suspended = suspended;
} /**
全部取消下载
*/
- (void)cancelAllDownloads {
[self.downloadQueue cancelAllOperations];
}
9.NSURLSessionTaskDelegate
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
// Identify the operation that runs this task and pass it the delegate method
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task]; [dataOperation URLSession:session task:task didCompleteWithError:error];
} - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler { completionHandler(request);
} - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { // Identify the operation that runs this task and pass it the delegate method
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task]; [dataOperation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler];
}
当收到数据的时候,会触发这些代理方法,最后调用SDWebImageDownloaderOperation
中的代理方法,来实际处理事情。
SDWebImage之SDWebImageDownloader的更多相关文章
- SDWebImage源码分析
1.概述 SDWebImage是iOS开发中,被广泛使用的一个第三方开源库,提供了图片从加载.解析.处理.缓存.清理等一些列功能,让我们能够专心于业务的处理.本篇会从SDWebImage的源码,来一步 ...
- SDWebImage源码解读之SDWebImageDownloader
SDWebImage源码解读之SDWebImageDownloader 第八篇 前言 SDWebImageDownloader这个类非常简单,作者的设计思路也很清晰,但是我想在这说点题外话. 如果有人 ...
- 【原】SDWebImage源码阅读(三)
[原]SDWebImage源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1.SDWebImageDownloader中的downloadImageWithURL 我们 ...
- 【原】SDWebImage源码阅读(二)
[原]SDWebImage源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 解决上一篇遗留的坑 上一篇中对sd_setImageWithURL函数简单分析了一下,还 ...
- IOS 网络-深入浅出(一 )-> 三方SDWebImage
首要我们以最为常用的UIImageView为例介绍实现原理: 1)UIImageView+WebCache: setImageWithURL:placeholderImage:options: 先显 ...
- 通读SDWebImage①--总体梳理、下载和缓存
本文目录 下载操作SDWebImageDownloaderOptions和下载过程实现 下载管理SDWebImageDownloader 缓存SDImageCache SDWebImageManage ...
- SDWebImage添加header
title: SDWebImage添加headerdate: 2016-03-07 17:32:57tags: SDWebImagecategories: IOS keywords: SDWebIma ...
- SDWebImage原理及使用(转)
转自http://www.cnblogs.com/jys509/p/5199997.html SDWebImage托管在github上.https://github.com/rs/SDWebImage ...
- SDWebImage使用及原理
第一步,下载SDWebImage,导入工程.github托管地址https://github.com/rs/SDWebImage 第二步,在需要的地方导入头文件 1 #import "UII ...
随机推荐
- 63.(原65)纯 CSS 创作一个摇摇晃晃的 loader
原文地址:https://segmentfault.com/a/1190000015424389 修改后地址:https://scrimba.com/c/cqKv4VCR HTML code: < ...
- java新手抖机灵(java新手技巧)
java新手抖机灵(java新手技巧) 1.交换两个整数的值 好处是不用定义临时变量,显得代码简洁,提高运行效率 其实也可以用+-*/进行这种运算 比如可以这样: a = a + b; b = a - ...
- itextsharp图片生成pdf模糊问题解释
I forget to mention that I' am using itextsharp 5.0.2. It turned out that PDF DPI = 110, which means ...
- 关于页面缩放时css错乱的处理方法---之一
这几天遇到一个问题,就是在做网页的时候,页面缩放时,布局就乱了,原来的样子不会跟随缩放的放大或者缩小进行改变,直接导致的后果,就是页面很难看,无法使用 之前虽然写了代码,但是一直没有注意到缩放后页面的 ...
- 零基础爬虫----python爬取豆瓣电影top250的信息(转)
今天利用xpath写了一个小爬虫,比较适合一些爬虫新手来学习.话不多说,开始今天的正题,我会利用一个案例来介绍下xpath如何对网页进行解析的,以及如何对信息进行提取的. python环境:pytho ...
- cdnbest节点动态ip配置教程
1.安装节点后,在未初始化里初始化节点,如下图操作,要选择动态ip(注:动态ip节点不支持添加辅ip) 服务器如果是动态ip,选择了动态ip选项,节点在自动更换了新的ip后,在节点列表里的ip和dns ...
- vue js库的条件渲染
条件渲染 通过条件指令可以控制元素的创建(显示)或者销毁(隐藏),常用的条件指令如下: v-if v-if可以控制元素的创建或者销毁 <h1 v-if="ok">Yes ...
- ExecuteNonQuery()
ExecuteNonQuery():执行一个SQL语句,返回受影响的行数,这个方法主要用于执行对数据库执行增加.更新.删除操作,注意查询的时候不是调用这个方法.用于完成insert,delete,up ...
- [Java学习]多线程
关于多进程与多线程 使用多进程的目的:提高CPU利用率. 使用多线程的目的:提高应用程序?利用率. 多线程与多进程区别:进程间内存独立:同一个进程的线程间共享"堆内存和方法区内存" ...
- 36. Valid Sudoku 判断九九有效的数独
[抄题]: Determine if a 9x9 Sudoku board is valid. Only the filled cells need to be validated according ...