SDWebImage源码阅读(九)SDWebImageDownloader
這篇學(xué)習(xí) 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 的枚舉,用來表示不同的下載選項。
當(dāng)需要給某個功能添加 Options 的時候或者對不同的情況分類的時候,一般使用枚舉來實現(xiàn)。
提供的了 9 種不同的情況,且他們都是可以根據(jù)不同的需求進(jìn)行匹配選擇。這里的給每一個選項賦值使用了掩碼,"<< " 表示左移操作,>> 表示右移操作。1<<0 ?表示把 1 轉(zhuǎn)化為二進(jìn)制然后整體左移 0 位,沒有變化當(dāng)然還是 1。1 << 1 則是把 1 轉(zhuǎn)化為二進(jìn)制是 0b0000 0001,把它整體左移 1 位是 0b0000 0010 它轉(zhuǎn)化為 10 進(jìn)制是 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 的枚舉,用來表示下載時數(shù)據(jù)被調(diào)用的順序。
FIFO (first-in-first-out 先進(jìn)先出)、LIFO (last-in-first-out 后進(jìn)先出),下載圖像一般按照放入隊列中的順序依次進(jìn)行,不過這里同時也支持后放入隊列任務(wù)的先下載的操作。
一個下載管理器應(yīng)該這樣管理下載,肯定有一個下載列表,可以假定這個列表保存在一個數(shù)組中,正常情況下應(yīng)該每次取出數(shù)組中第1個元素來下載,這就是 FIFO (先進(jìn)先出)。那么要改為 LIFO (后進(jìn)先出),應(yīng)該是針對某一個下載的,不應(yīng)該是把取出數(shù)據(jù)的順序改為從數(shù)組的最后一個元素取出。
?
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 ,一個是下載進(jìn)度的 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這個類表示與每一個下載相關(guān)的?token,能用于取消一個下載。
SDWebImageDownloadToken?作為每一個下載的唯一身份標(biāo)識。SDWebImageDownloader?和我們平時開發(fā)中的下載還是有不一樣的地方的,它弱化了下載過程,比較強調(diào)的是下載結(jié)果。不支持?jǐn)帱c下載(當(dāng)然這可能沒有必要)。?
如果需要設(shè)計一個自己的下載管理者,就應(yīng)該設(shè)計一個類似 SDWebImageDownloadToken?這樣的下載對象封裝類,需要的所有信息,都能在這個對象中獲取。
SDWebImageDownloader
1 /** 2 * Asynchronous downloader dedicated and optimized for image loading. 3 */ 4 @interface SDWebImageDownloader : NSObject異步下載專用的圖像加載優(yōu)化。
?
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;是否解壓圖像,解壓下載和緩存的圖像可以提高性能,但是會消耗大量的內(nèi)存,默認(rèn)是 YES,如果因消耗內(nèi)存過大可以設(shè)置為 NO。
?
maxConcurrentDownloads
1 /** 2 * The maximum number of concurrent downloads 3 */ 4 @property (assign, nonatomic) NSInteger maxConcurrentDownloads;表示并發(fā)下載的最大數(shù)量。
?
currentDownloadCount
1 /** 2 * Shows the current amount of downloads that still need to be downloaded 3 */ 4 @property (readonly, nonatomic) NSUInteger currentDownloadCount;顯示當(dāng)前仍需要下載的下載量。
?
downloadTimeout
1 /** 2 * The timeout value (in seconds) for the download operation. Default: 15.0. 3 */ 4 @property (assign, nonatomic) NSTimeInterval downloadTimeout;下載操作的超時時間,單位是秒。默認(rèn)是 15 秒。
?
executionOrder
1 /** 2 * Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`. 3 */ 4 @property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;更改下載操作的執(zhí)行順序。默認(rèn)是 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;設(shè)置默認(rèn)的 URL 憑據(jù),以設(shè)置請求操作。
?
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;設(shè)置篩選器以選擇用于下載圖像 http 請求的標(biāo)頭。該 block 將被調(diào)用為每個下載圖像請求,返回一個 NSDictionary 作為相應(yīng)的 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 實例做參數(shù),創(chuàng)建一個具有指定的 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;設(shè)置附加到每個下載 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 創(chuàng)建一個 SDWebImageDownloader 異步下載實例。
當(dāng)圖像下載完成和下載錯誤時,將通知代理。
?
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;設(shè)置下載隊列掛起狀態(tài)。
?
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 @endNSOperationQueue *downloadQueue 下載的隊列。
NSOperation *lastAddedOperation 用于紀(jì)錄最后添加的操作。
Class operationClass ?支持自定義的操作類。
NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> URLOperations 存放著所有的 operation。
SDHTTPHeadersMutableDictionary *HTTPHeaders HTTP 請求頭。
dispatch_queue_t barrierQueue 這個隊列是用于序列化所有下載操作的網(wǎng)絡(luò)響應(yīng)的處理在一個單一的隊列。
NSURLSession *session 數(shù)據(jù)任務(wù)將運行的的會話。
?
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 這兩個方法:
執(zhí)行時機: load 在程序運行后立即執(zhí)行 initialize 在類的方法第一次被調(diào)用時執(zhí)行
若自身未定義,是否沿用父類的方法:load 否 initialize 是
類別中的定義: load 全部執(zhí)行,但后于類中的方法,initialize 覆蓋類中的方法,只執(zhí)行一個
?
上面 initialize 里面的方法實現(xiàn),是為了在圖片下載的時候綁定一個 SDNetworkActivityIndicator,當(dāng)引入 SDNetworkActivityIndicator 類的時候,先獲得 SDNetworkActivityIndicator 的單例類,然后首先移除之前添加的?SDWebImageDownloadStartNotification、SDWebImageDownloadStopNotification?的通知,然后重新添加 Start 和 Stop 通知,當(dāng) Start 的時候執(zhí)行 startActivity 方法,當(dāng) Stop 的時候執(zhí)行 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 }單例方法實現(xiàn)。
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 }自定義初始化方法實現(xiàn)。對上面提到的屬性默認(rèn)賦值。SDWebImageDownloaderOperation 為自定義類、默認(rèn)解壓、下載順序 FIFO、并發(fā)下載是 6、15 秒超時等等。
_HTTPHeaders 比較特殊:
"image/webp,image/*;q=0.8"?是什么意思??image/webp?是 web 格式的圖片,q=0.8?指的是權(quán)重系數(shù)為0.8,q 的取值范圍是 0 - 1, 默認(rèn)值為 1,q 作用于它前邊分號(;)前邊的內(nèi)容。在這里,image/webp,image/*;q=0.8?表示優(yōu)先接受?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 方法實現(xiàn)
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 都會被作為對應(yīng)的下載操作的唯一標(biāo)識,每一個下載都會綁定一個 progressBlock 和一個 completeBlock,最后還用這個 URL 與 SDWebImageDownloaderOperation 建立聯(lián)系。
該 URL 將被作為回調(diào)字典的關(guān)鍵所以不能為 nil。如果是 nil 則直接調(diào)用沒有數(shù)據(jù)和 image 的完成的 block。
定義一個 __block 修飾的 SDWebImageDownloadToken 實例 token。
在 self.barrierQueue 隊列中使用?dispatch_barrier_sync?同步執(zhí)行:
使用 URL 從 self.URLOperations 中獲取指定的 SDWebImageDownloaderOperation。
如果 operation 不存在:
operation = createCallback(); 執(zhí)行這個 block。
把 operation 存入 self.URLOperations 字典里面。
operation 的 ?completionBlock 的實現(xiàn),從 self.URLOperations 里面刪除 URL 對應(yīng)的 SDWebImageDownloaderOperation。
把完成的 block 和 進(jìn)度的 block 添加進(jìn) operation 的 _callbackBlocks 里面。
downloadOperationCancelToken 是一個存放了完成 block 和進(jìn)度 block 的字典。
給 token 賦值。
返回 token。
?
創(chuàng)建 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。
調(diào)用上面的返回 Token 的方法:
主要看 createCallback 的實現(xiàn):
在 block 內(nèi)部使用 __strong 修飾 self。
設(shè)置超時時間。
創(chuàng)建 request,主要注意緩存策略的選擇。
給 ?request 的 ?HTTPShouldHandleCookies 和 HTTPShouldUsePipelining 賦值。
設(shè)置請求頭。
創(chuàng)建 SDWebImageDownloaderOperation。
給 SDWebImageDownloaderOperation 設(shè)置 urlCredential。
給 SDWebImageDownloaderOperation 設(shè)置操作的優(yōu)先級。
把操作添加進(jìn)隊列里面。
根據(jù) _executionOrder 是先進(jìn)先出還是先進(jìn)后出,給操作添加依賴。
?
取消某個操作
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 }
設(shè)置隊列掛起
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 設(shè)置為了當(dāng)前類,且 SDWebImageDownloader 遵守?NSURLSessionTaskDelegate、NSURLSessionDataDelegate?協(xié)議,當(dāng)使用 self.session 請求數(shù)據(jù),收到響應(yīng)的的時候,會調(diào)用 SDWebImageDownloader.m 里面實現(xiàn)的代理方法,然后在調(diào)用 SDWebImageDownloaderOperation 里面的代理方法。第一次見這樣的設(shè)計,作者的目的是為了共用一個 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
?
轉(zhuǎn)載于:https://www.cnblogs.com/chmhml/p/6956727.html
總結(jié)
以上是生活随笔為你收集整理的SDWebImage源码阅读(九)SDWebImageDownloader的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Java经典编程题50道之三十四
- 下一篇: yum安装时出现:Cannot retr
