SDWebImage源码阅读(九)SDWebImageDownloader
这篇学习 SDWebImageDownloader 这个类。
首先依然是看 SDWebImageDownloader.h:
SDWebImageDownloaderOptions
1 typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) { 2 SDWebImageDownloaderLowPriority = 1 << 0, 3 SDWebImageDownloaderProgressiveDownload = 1 << 1, 4 5 /** 6 * By default, request prevent the use of NSURLCache. With this flag, NSURLCache 7 * is used with default policies. 8 */ 9 SDWebImageDownloaderUseNSURLCache = 1 << 2, 10 11 /** 12 * Call completion block with nil image/imageData if the image was read from NSURLCache 13 * (to be combined with `SDWebImageDownloaderUseNSURLCache`). 14 */ 15 16 SDWebImageDownloaderIgnoreCachedResponse = 1 << 3, 17 /** 18 * In iOS 4+, continue the download of the image if the app goes to background. This is achieved by asking the system for 19 * extra time in background to let the request finish. If the background task expires the operation will be cancelled. 20 */ 21 22 SDWebImageDownloaderContinueInBackground = 1 << 4, 23 24 /** 25 * Handles cookies stored in NSHTTPCookieStore by setting 26 * NSMutableURLRequest.HTTPShouldHandleCookies = YES; 27 */ 28 SDWebImageDownloaderHandleCookies = 1 << 5, 29 30 /** 31 * Enable to allow untrusted SSL certificates. 32 * Useful for testing purposes. Use with caution in production. 33 */ 34 SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6, 35 36 /** 37 * Put the image in the high priority queue. 38 */ 39 SDWebImageDownloaderHighPriority = 1 << 7, 40 41 /** 42 * Scale down the image 43 */ 44 SDWebImageDownloaderScaleDownLargeImages = 1 << 8, 45 };
这是一个 NS_OPTIONS 的枚举,用来表示不同的下载选项。
当需要给某个功能添加 Options 的时候或者对不同的情况分类的时候,一般使用枚举来实现。
提供的了 9 种不同的情况,且他们都是可以根据不同的需求进行匹配选择。这里的给每一个选项赋值使用了掩码,"<< " 表示左移操作,>> 表示右移操作。1<<0 表示把 1 转化为二进制然后整体左移 0 位,没有变化当然还是 1。1 << 1 则是把 1 转化为二进制是 0b0000 0001,把它整体左移 1 位是 0b0000 0010 它转化为 10 进制是 2,即左移一位表示在原来的值上乘以 2 。所以上面表示的枚举值自上而下分别是 1 左移 0 位至 8 位,表示的 NSUInteger 分别是: 0b0000 0001、0b0000 0010、0b0000 0100、0b0000 1000、0b0001 0000、0b0010 0000、0b0100 0000、0b1000 000 ...
所以在判断 self.option 是否是某个枚举值的时候,直接拿 self.option 与要判断的枚举值做 与 操作:
1 self.option & SDWebImageDownloaderIgnoreCacheResponse
SDWebImageDownloaderExecutionOrder
1 typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) { 2 /** 3 * Default value. All download operations will execute in queue style (first-in-first-out). 4 */ 5 SDWebImageDownloaderFIFOExecutionOrder, 6 7 /** 8 * All download operations will execute in stack style (last-in-first-out). 9 */ 10 SDWebImageDownloaderLIFOExecutionOrder 11 };
这是一个 NS_ENUM 的枚举,用来表示下载时数据被调用的顺序。
FIFO (first-in-first-out 先进先出)、LIFO (last-in-first-out 后进先出),下载图像一般按照放入队列中的顺序依次进行,不过这里同时也支持后放入队列任务的先下载的操作。
一个下载管理器应该这样管理下载,肯定有一个下载列表,可以假定这个列表保存在一个数组中,正常情况下应该每次取出数组中第1个元素来下载,这就是 FIFO (先进先出)。那么要改为 LIFO (后进先出),应该是针对某一个下载的,不应该是把取出数据的顺序改为从数组的最后一个元素取出。
SDWebImageDownloadStartNotification、
SDWebImageDownloadStopNotification
1 extern NSString * _Nonnull const SDWebImageDownloadStartNotification; 2 extern NSString * _Nonnull const SDWebImageDownloadStopNotification;
它们两个的赋值在 SDWebImageDownloaderOpertion.m 里面:
1 NSString *const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification"; 2 NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification";
SDWebImageDownloaderProgressBlock、
SDWebImageDownloaderCompletedBlock
1 typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL); 2 3 typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished);
命名两个 block ,一个是下载进度的 block,一个下载完成的 block。
SDHTTPHeadersDictionary、
SDHTTPHeadersMutableDictionary
1 typedef NSDictionary<NSString *, NSString *> SDHTTPHeadersDictionary; 2 typedef NSMutableDictionary<NSString *, NSString *> SDHTTPHeadersMutableDictionary;
命名两个 key 和 value 都是字符串,用于 SDHTTPHeaders 的字典。
SDWebImageDownloaderHeadersFilterBlock
1 typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterBlock)(NSURL * _Nullable url, SDHTTPHeadersDictionary * _Nullable headers);
这个 block 用来自定义请求头。
SDWebImageDownloadToken
1 @interface SDWebImageDownloadToken : NSObject 2 3 @property (nonatomic, strong, nullable) NSURL *url; 4 @property (nonatomic, strong, nullable) id downloadOperationCancelToken; 5 6 @end
这个类表示与每一个下载相关的 token,能用于取消一个下载。
SDWebImageDownloadToken 作为每一个下载的唯一身份标识。SDWebImageDownloader 和我们平时开发中的下载还是有不一样的地方的,它弱化了下载过程,比较强调的是下载结果。不支持断点下载(当然这可能没有必要)。
如果需要设计一个自己的下载管理者,就应该设计一个类似 SDWebImageDownloadToken 这样的下载对象封装类,需要的所有信息,都能在这个对象中获取。
SDWebImageDownloader
1 /** 2 * Asynchronous downloader dedicated and optimized for image loading. 3 */ 4 @interface SDWebImageDownloader : NSObject
异步下载专用的图像加载优化。
shouldDecompressImages
1 /** 2 * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory. 3 * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption. 4 */ 5 @property (assign, nonatomic) BOOL shouldDecompressImages;
是否解压图像,解压下载和缓存的图像可以提高性能,但是会消耗大量的内存,默认是 YES,如果因消耗内存过大可以设置为 NO。
maxConcurrentDownloads
1 /** 2 * The maximum number of concurrent downloads 3 */ 4 @property (assign, nonatomic) NSInteger maxConcurrentDownloads;
表示并发下载的最大数量。
currentDownloadCount
1 /** 2 * Shows the current amount of downloads that still need to be downloaded 3 */ 4 @property (readonly, nonatomic) NSUInteger currentDownloadCount;
显示当前仍需要下载的下载量。
downloadTimeout
1 /** 2 * The timeout value (in seconds) for the download operation. Default: 15.0. 3 */ 4 @property (assign, nonatomic) NSTimeInterval downloadTimeout;
下载操作的超时时间,单位是秒。默认是 15 秒。
executionOrder
1 /** 2 * Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`. 3 */ 4 @property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;
更改下载操作的执行顺序。默认是 FIFO。
sharedDownloader
1 /** 2 * Singleton method, returns the shared instance 3 * 4 * @return global shared instance of downloader class 5 */ 6 + (nonnull instancetype)sharedDownloader;
单例方法。
urlCredential
1 /** 2 * Set the default URL credential to be set for request operations. 3 */ 4 @property (strong, nonatomic, nullable) NSURLCredential *urlCredential;
设置默认的 URL 凭据,以设置请求操作。
username、password
1 /** 2 * Set username 3 */ 4 @property (strong, nonatomic, nullable) NSString *username; 5 6 /** 7 * Set password 8 */ 9 @property (strong, nonatomic, nullable) NSString *password;
headersFilter
1 /** 2 * Set filter to pick headers for downloading image HTTP request. 3 * 4 * This block will be invoked for each downloading image request, returned 5 * NSDictionary will be used as headers in corresponding HTTP request. 6 */ 7 @property (nonatomic, copy, nullable) SDWebImageDownloaderHeadersFilterBlock headersFilter;
设置筛选器以选择用于下载图像 http 请求的标头。该 block 将被调用为每个下载图像请求,返回一个 NSDictionary 作为相应的 http 请求头。
Method
1 /** 2 * Creates an instance of a downloader with specified session configuration. 3 * *Note*: `timeoutIntervalForRequest` is going to be overwritten. 4 * @return new instance of downloader class 5 */ 6 - (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration NS_DESIGNATED_INITIALIZER;
使用一个 NSURLSessionConfiguration 实例做参数,创建一个具有指定的 NSURLSessionConfiguration 的下载实例。
使用 NS_DESIGNATED_INITIALIZER 表示指定的初始化方法。
1 /** 2 * Set a value for a HTTP header to be appended to each download HTTP request. 3 * 4 * @param value The value for the header field. Use `nil` value to remove the header. 5 * @param field The name of the header field to set. 6 */ 7 - (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field;
设置附加到每个下载 HTTP 请求的 HTTP 头的值。
1 /** 2 * Returns the value of the specified HTTP header field. 3 * 4 * @return The value associated with the header field field, or `nil` if there is no corresponding header field. 5 */ 6 - (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field;
返回指定的 HTTP 头字段的的值。
1 /** 2 * Sets a subclass of `SDWebImageDownloaderOperation` as the default 3 * `NSOperation` to be used each time SDWebImage constructs a request 4 * operation to download an image. 5 * 6 * @param operationClass The subclass of `SDWebImageDownloaderOperation` to set 7 * as default. Passing `nil` will revert to `SDWebImageDownloaderOperation`. 8 */ 9 - (void)setOperationClass:(nullable Class)operationClass;
1 /** 2 * Creates a SDWebImageDownloader async downloader instance with a given URL 3 * 4 * The delegate will be informed when the image is finish downloaded or an error has happen. 5 * 6 * @see SDWebImageDownloaderDelegate 7 * 8 * @param url The URL to the image to download 9 * @param options The options to be used for this download 10 * @param progressBlock A block called repeatedly while the image is downloading 11 * @note the progress block is executed on a background queue 12 * @param completedBlock A block called once the download is completed. 13 * If the download succeeded, the image parameter is set, in case of error, 14 * error parameter is set with the error. The last parameter is always YES 15 * if SDWebImageDownloaderProgressiveDownload isn't use. With the 16 * SDWebImageDownloaderProgressiveDownload option, this block is called 17 * repeatedly with the partial image object and the finished argument set to NO 18 * before to be called a last time with the full image and finished argument 19 * set to YES. In case of error, the finished argument is always YES. 20 * 21 * @return A token (SDWebImageDownloadToken) that can be passed to -cancel: to cancel this operation 22 */ 23 - (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url 24 options:(SDWebImageDownloaderOptions)options 25 progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 26 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
使用指定的 URL 创建一个 SDWebImageDownloader 异步下载实例。
当图像下载完成和下载错误时,将通知代理。
1 /** 2 * Cancels a download that was previously queued using -downloadImageWithURL:options:progress:completed: 3 * 4 * @param token The token received from -downloadImageWithURL:options:progress:completed: that should be canceled. 5 */ 6 - (void)cancel:(nullable SDWebImageDownloadToken *)token;
取消先前排队使用的下载。
1 /** 2 * Sets the download queue suspension state 3 */ 4 - (void)setSuspended:(BOOL)suspended;
设置下载队列挂起状态。
1 /** 2 * Cancels all download operations in the queue 3 */ 4 - (void)cancelAllDownloads;
取消队列中的所有下载操作。
SDWebImageDownloader.m
1 @implementation SDWebImageDownloadToken 2 @end 3 4 5 @interface SDWebImageDownloader () <NSURLSessionTaskDelegate, NSURLSessionDataDelegate> 6 7 @property (strong, nonatomic, nonnull) NSOperationQueue *downloadQueue; 8 @property (weak, nonatomic, nullable) NSOperation *lastAddedOperation; 9 @property (assign, nonatomic, nullable) Class operationClass; 10 @property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations; 11 @property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders; 12 // This queue is used to serialize the handling of the network responses of all the download operation in a single queue 13 @property (SDDispatchQueueSetterSementics, nonatomic, nullable) dispatch_queue_t barrierQueue; 14 15 // The session in which data tasks will run 16 @property (strong, nonatomic) NSURLSession *session; 17 18 @end
NSOperationQueue *downloadQueue 下载的队列。
NSOperation *lastAddedOperation 用于纪录最后添加的操作。
Class operationClass 支持自定义的操作类。
NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> URLOperations 存放着所有的 operation。
SDHTTPHeadersMutableDictionary *HTTPHeaders HTTP 请求头。
dispatch_queue_t barrierQueue 这个队列是用于序列化所有下载操作的网络响应的处理在一个单一的队列。
NSURLSession *session 数据任务将运行的的会话。
initialize
1 + (void)initialize { 2 // Bind SDNetworkActivityIndicator if available (download it here: http://github.com/rs/SDNetworkActivityIndicator ) 3 // To use it, just add #import "SDNetworkActivityIndicator.h" in addition to the SDWebImage import 4 if (NSClassFromString(@"SDNetworkActivityIndicator")) { 5 6 #pragma clang diagnostic push 7 #pragma clang diagnostic ignored "-Warc-performSelector-leaks" 8 id activityIndicator = [NSClassFromString(@"SDNetworkActivityIndicator") performSelector:NSSelectorFromString(@"sharedActivityIndicator")]; 9 #pragma clang diagnostic pop 10 11 // Remove observer in case it was previously added. 12 [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStartNotification object:nil]; 13 [[NSNotificationCenter defaultCenter] removeObserver:activityIndicator name:SDWebImageDownloadStopNotification object:nil]; 14 15 [[NSNotificationCenter defaultCenter] addObserver:activityIndicator 16 selector:NSSelectorFromString(@"startActivity") 17 name:SDWebImageDownloadStartNotification object:nil]; 18 [[NSNotificationCenter defaultCenter] addObserver:activityIndicator 19 selector:NSSelectorFromString(@"stopActivity") 20 name:SDWebImageDownloadStopNotification object:nil]; 21 } 22 }
initialize 和 load 这两个方法:
执行时机: load 在程序运行后立即执行 initialize 在类的方法第一次被调用时执行
若自身未定义,是否沿用父类的方法:load 否 initialize 是
类别中的定义: load 全部执行,但后于类中的方法,initialize 覆盖类中的方法,只执行一个
上面 initialize 里面的方法实现,是为了在图片下载的时候绑定一个 SDNetworkActivityIndicator,当引入 SDNetworkActivityIndicator 类的时候,先获得 SDNetworkActivityIndicator 的单例类,然后首先移除之前添加的 SDWebImageDownloadStartNotification、SDWebImageDownloadStopNotification 的通知,然后重新添加 Start 和 Stop 通知,当 Start 的时候执行 startActivity 方法,当 Stop 的时候执行 stopActivity 方法。
sharedDownloader
1 + (nonnull instancetype)sharedDownloader { 2 static dispatch_once_t once; 3 static id instance; 4 dispatch_once(&once, ^{ 5 instance = [self new]; 6 }); 7 return instance; 8 }
单例方法实现。
init
1 - (nonnull instancetype)init { 2 return [self initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; 3 } 4 5 - (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration { 6 if ((self = [super init])) { 7 _operationClass = [SDWebImageDownloaderOperation class]; 8 _shouldDecompressImages = YES; 9 _executionOrder = SDWebImageDownloaderFIFOExecutionOrder; 10 _downloadQueue = [NSOperationQueue new]; 11 _downloadQueue.maxConcurrentOperationCount = 6; 12 _downloadQueue.name = @"com.hackemist.SDWebImageDownloader"; 13 _URLOperations = [NSMutableDictionary new]; 14 #ifdef SD_WEBP 15 _HTTPHeaders = [@{@"Accept": @"image/webp,image/*;q=0.8"} mutableCopy]; 16 #else 17 _HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy]; 18 #endif 19 _barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT); 20 _downloadTimeout = 15.0; 21 22 sessionConfiguration.timeoutIntervalForRequest = _downloadTimeout; 23 24 /** 25 * Create the session for this task 26 * We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate 27 * method calls and completion handler calls. 28 */ 29 self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration 30 delegate:self 31 delegateQueue:nil]; 32 } 33 return self; 34 }
自定义初始化方法实现。对上面提到的属性默认赋值。SDWebImageDownloaderOperation 为自定义类、默认解压、下载顺序 FIFO、并发下载是 6、15 秒超时等等。
_HTTPHeaders 比较特殊:
"image/webp,image/*;q=0.8" 是什么意思?
image/webp 是 web 格式的图片,
q=0.8 指的是权重系数为0.8,q 的取值范围是 0 - 1, 默认值为 1,q 作用于它前边分号(
;)前边的内容。在这里,
image/webp,image/*;q=0.8 表示优先接受
image/webp,其次接受
image/* 的图片。
dealloc
1 - (void)dealloc { 2 [self.session invalidateAndCancel]; 3 self.session = nil; 4 5 [self.downloadQueue cancelAllOperations]; 6 SDDispatchQueueRelease(_barrierQueue); 7 }
.h 里面的一堆 SET 和 GET 方法实现
1 - (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field { 2 if (value) { 3 self.HTTPHeaders[field] = value; 4 } 5 else { 6 [self.HTTPHeaders removeObjectForKey:field]; 7 } 8 } 9 10 - (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field { 11 return self.HTTPHeaders[field]; 12 } 13 14 - (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads { 15 _downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads; 16 } 17 18 - (NSUInteger)currentDownloadCount { 19 return _downloadQueue.operationCount; 20 } 21 22 - (NSInteger)maxConcurrentDownloads { 23 return _downloadQueue.maxConcurrentOperationCount; 24 } 25 26 - (void)setOperationClass:(nullable Class)operationClass { 27 if (operationClass && [operationClass isSubclassOfClass:[NSOperation class]] && [operationClass conformsToProtocol:@protocol(SDWebImageDownloaderOperationInterface)]) { 28 _operationClass = operationClass; 29 } else { 30 _operationClass = [SDWebImageDownloaderOperation class]; 31 } 32 }
token
1 - (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock 2 completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock 3 forURL:(nullable NSURL *)url 4 createCallback:(SDWebImageDownloaderOperation *(^)())createCallback { 5 // 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. 6 if (url == nil) { 7 if (completedBlock != nil) { 8 completedBlock(nil, nil, nil, NO); 9 } 10 return nil; 11 } 12 13 __block SDWebImageDownloadToken *token = nil; 14 15 dispatch_barrier_sync(self.barrierQueue, ^{ 16 SDWebImageDownloaderOperation *operation = self.URLOperations[url]; 17 if (!operation) { 18 operation = createCallback(); 19 self.URLOperations[url] = operation; 20 21 __weak SDWebImageDownloaderOperation *woperation = operation; 22 operation.completionBlock = ^{ 23 SDWebImageDownloaderOperation *soperation = woperation; 24 if (!soperation) return; 25 if (self.URLOperations[url] == soperation) { 26 [self.URLOperations removeObjectForKey:url]; 27 }; 28 }; 29 } 30 id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock]; 31 32 token = [SDWebImageDownloadToken new]; 33 token.url = url; 34 token.downloadOperationCancelToken = downloadOperationCancelToken; 35 }); 36 37 return token; 38 }
每一个图片的 URL 都会被作为对应的下载操作的唯一标识,每一个下载都会绑定一个 progressBlock 和一个 completeBlock,最后还用这个 URL 与 SDWebImageDownloaderOperation 建立联系。
该 URL 将被作为回调字典的关键所以不能为 nil。如果是 nil 则直接调用没有数据和 image 的完成的 block。
定义一个 __block 修饰的 SDWebImageDownloadToken 实例 token。
在 self.barrierQueue 队列中使用 dispatch_barrier_sync 同步执行:
使用 URL 从 self.URLOperations 中获取指定的 SDWebImageDownloaderOperation。
如果 operation 不存在:
operation = createCallback(); 执行这个 block。
把 operation 存入 self.URLOperations 字典里面。
operation 的 completionBlock 的实现,从 self.URLOperations 里面删除 URL 对应的 SDWebImageDownloaderOperation。
把完成的 block 和 进度的 block 添加进 operation 的 _callbackBlocks 里面。
downloadOperationCancelToken 是一个存放了完成 block 和进度 block 的字典。
给 token 赋值。
返回 token。
创建 SDWebImageDownloaderOperation
1 - (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url 2 options:(SDWebImageDownloaderOptions)options 3 progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock 4 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock { 5 __weak SDWebImageDownloader *wself = self; 6 7 return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{ 8 __strong __typeof (wself) sself = wself; 9 NSTimeInterval timeoutInterval = sself.downloadTimeout; 10 if (timeoutInterval == 0.0) { 11 timeoutInterval = 15.0; 12 } 13 14 // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise 15 NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval]; 16 request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies); 17 request.HTTPShouldUsePipelining = YES; 18 if (sself.headersFilter) { 19 request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]); 20 } 21 else { 22 request.allHTTPHeaderFields = sself.HTTPHeaders; 23 } 24 SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options]; 25 operation.shouldDecompressImages = sself.shouldDecompressImages; 26 27 if (sself.urlCredential) { 28 operation.credential = sself.urlCredential; 29 } else if (sself.username && sself.password) { 30 operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession]; 31 } 32 33 if (options & SDWebImageDownloaderHighPriority) { 34 operation.queuePriority = NSOperationQueuePriorityHigh; 35 } else if (options & SDWebImageDownloaderLowPriority) { 36 operation.queuePriority = NSOperationQueuePriorityLow; 37 } 38 39 [sself.downloadQueue addOperation:operation]; 40 if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) { 41 // Emulate LIFO execution order by systematically adding new operations as last operation's dependency 42 [sself.lastAddedOperation addDependency:operation]; 43 sself.lastAddedOperation = operation; 44 } 45 46 return operation; 47 }]; 48 }
用 __weak 修饰 self。
调用上面的返回 Token 的方法:
主要看 createCallback 的实现:
在 block 内部使用 __strong 修饰 self。
设置超时时间。
创建 request,主要注意缓存策略的选择。
给 request 的 HTTPShouldHandleCookies 和 HTTPShouldUsePipelining 赋值。
设置请求头。
创建 SDWebImageDownloaderOperation。
给 SDWebImageDownloaderOperation 设置 urlCredential。
给 SDWebImageDownloaderOperation 设置操作的优先级。
把操作添加进队列里面。
根据 _executionOrder 是先进先出还是先进后出,给操作添加依赖。
取消某个操作
1 - (void)cancel:(nullable SDWebImageDownloadToken *)token { 2 dispatch_barrier_async(self.barrierQueue, ^{ 3 SDWebImageDownloaderOperation *operation = self.URLOperations[token.url]; 4 BOOL canceled = [operation cancel:token.downloadOperationCancelToken]; 5 if (canceled) { 6 [self.URLOperations removeObjectForKey:token.url]; 7 } 8 }); 9 }
设置队列挂起
1 - (void)setSuspended:(BOOL)suspended { 2 (self.downloadQueue).suspended = suspended; 3 }
全部操作取消
1 - (void)cancelAllDownloads { 2 [self.downloadQueue cancelAllOperations]; 3 }
Helper methods
1 #pragma mark Helper methods 2 3 - (SDWebImageDownloaderOperation *)operationWithTask:(NSURLSessionTask *)task { 4 SDWebImageDownloaderOperation *returnOperation = nil; 5 for (SDWebImageDownloaderOperation *operation in self.downloadQueue.operations) { 6 if (operation.dataTask.taskIdentifier == task.taskIdentifier) { 7 returnOperation = operation; 8 break; 9 } 10 } 11 return returnOperation; 12 }
NSURLSessionDataDelegate
self.session 在初始化的时候把 delegate 设置为了当前类,且 SDWebImageDownloader 遵守 NSURLSessionTaskDelegate、NSURLSessionDataDelegate 协议,当使用 self.session 请求数据,收到响应的的时候,会调用 SDWebImageDownloader.m 里面实现的代理方法,然后在调用 SDWebImageDownloaderOperation 里面的代理方法。第一次见这样的设计,作者的目的是为了共用一个 URLSession。
1 - (void)URLSession:(NSURLSession *)session 2 dataTask:(NSURLSessionDataTask *)dataTask 3 didReceiveResponse:(NSURLResponse *)response 4 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler { 5 6 // Identify the operation that runs this task and pass it the delegate method 7 SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask]; 8 9 [dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler]; 10 } 11 12 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { 13 14 // Identify the operation that runs this task and pass it the delegate method 15 SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask]; 16 17 [dataOperation URLSession:session dataTask:dataTask didReceiveData:data]; 18 } 19 20 - (void)URLSession:(NSURLSession *)session 21 dataTask:(NSURLSessionDataTask *)dataTask 22 willCacheResponse:(NSCachedURLResponse *)proposedResponse 23 completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler { 24 25 // Identify the operation that runs this task and pass it the delegate method 26 SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask]; 27 28 [dataOperation URLSession:session dataTask:dataTask willCacheResponse:proposedResponse completionHandler:completionHandler]; 29 }
NSURLSessionDataDelegate
1 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { 2 // Identify the operation that runs this task and pass it the delegate method 3 SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task]; 4 5 [dataOperation URLSession:session task:task didCompleteWithError:error]; 6 } 7 8 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler { 9 10 completionHandler(request); 11 } 12 13 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler { 14 15 // Identify the operation that runs this task and pass it the delegate method 16 SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:task]; 17 18 [dataOperation URLSession:session task:task didReceiveChallenge:challenge completionHandler:completionHandler]; 19 }
参考链接:http://www.jianshu.com/p/8411e4645f0d
END