第八篇
前言
SDWebImageDownloader
這個(gè)類非常簡(jiǎn)單朴摊,作者的設(shè)計(jì)思路也很清晰句占,但是我想在這說點(diǎn)題外話岛杀。
如果有人問你:你怎么看待編程這件事诱篷?你怎么回答壶唤。這個(gè)問題是我在看這個(gè)類的時(shí)候,忽然出現(xiàn)在我腦子中的棕所。我突然意識(shí)到闸盔,其實(shí)不管是函數(shù)還是屬性,他們都是數(shù)據(jù)
橙凳。我們編寫的所有程序都是在處理數(shù)據(jù)
蕾殴。函數(shù)本身也是一種特殊的數(shù)據(jù)
。
真正難的是生產(chǎn)數(shù)據(jù)
的這一過程岛啸。舉個(gè)例子钓觉,給你一堆菜籽,要求生產(chǎn)出油來(lái)坚踩。怎么辦荡灾?我們首先為這個(gè)任務(wù)設(shè)計(jì)一個(gè)函數(shù):
- (油)用菜籽生產(chǎn)油(菜籽);
這就是我們最外層的函數(shù)瞬铸,也應(yīng)該是我們最開始想到的函數(shù)批幌。然后經(jīng)過我們的研究發(fā)現(xiàn),這個(gè)生產(chǎn)過程很復(fù)雜嗓节,必須分工合作才能實(shí)現(xiàn)荧缘。于是我們把這個(gè)任務(wù)分割為好幾個(gè)小任務(wù):
1. - (干凈的菜籽)取出雜質(zhì)(菜籽);
2. - (炒熟的菜籽)把菜籽炒一下(干凈的菜籽);
3. - (蒸了的菜籽)把菜籽蒸一下(炒熟的菜籽);
4. - (捆好的菜籽)把菜籽包捆成一塊(蒸了的菜籽);
5. - (油)撞擊菜籽包(捆好的菜籽);
大家有沒有發(fā)現(xiàn),整個(gè)榨油的過程就是對(duì)數(shù)據(jù)的處理拦宣。這一點(diǎn)其實(shí)很重要截粗。如果沒有把- (油)用菜籽生產(chǎn)油(菜籽);
這一任務(wù)進(jìn)行拆分鸵隧,我們就會(huì)寫出復(fù)雜無(wú)比的函數(shù)绸罗。那么就有人要問了,只要實(shí)現(xiàn)這個(gè)功能就行了唄豆瘫。其實(shí)這往往是寫不出好代碼的原因珊蟀。
整個(gè)任務(wù)的設(shè)計(jì)應(yīng)該是事先就設(shè)計(jì)好的。任務(wù)被分割成更小更簡(jiǎn)單的部分外驱,然后再去實(shí)現(xiàn)這些最小的任務(wù)育灸,不應(yīng)該是編寫邊分割任務(wù),往往臨時(shí)分割的任務(wù)(也算是私有函數(shù)吧)沒有最正確的界限昵宇。
有了上邊合理的分工之后呢描扯,我們就可以進(jìn)行任務(wù)安排了。我們回到現(xiàn)實(shí)開發(fā)中來(lái)趟薄。上邊5個(gè)子任務(wù)的難度是不同的。有的人可能基礎(chǔ)比較差典徊,那么讓他去干篩菜籽這種體力活杭煎,應(yīng)該沒問題恩够。那些炒或者蒸的子任務(wù)是要掌握火候的,也就是說有點(diǎn)技術(shù)含量羡铲。那么就交給能勝任這項(xiàng)工作的人去做蜂桶。所有的這一切,我們只要事先定義好各自的生產(chǎn)結(jié)果就行了也切,完全不影響每個(gè)程序的執(zhí)行扑媚。
怎么樣?大家體會(huì)到這種編程設(shè)計(jì)的好處了嗎雷恃?我還可以進(jìn)行合并疆股,把炒和煮合成一個(gè)小組,完全可行啊倒槐。好了這方面的思考就說這么多吧旬痹。如果我想買煮熟了的菜籽,是不是也很簡(jiǎn)單讨越?
有的人用原始的撞擊菜籽包榨油两残,有的人卻用最先進(jìn)的儀器榨油,這就是編程技術(shù)和知識(shí)深度的區(qū)別啊把跨。
SDWebImageDownloaderOptions
言歸正傳人弓,當(dāng)我們需要給某個(gè)功能添加Options的時(shí)候,一般使用枚舉來(lái)實(shí)現(xiàn)着逐。我們先看看支持的選項(xiàng):
typedef NS_OPTIONS(NSUInteger, SDWebImageDownloaderOptions) {
SDWebImageDownloaderLowPriority = 1 << 0,
SDWebImageDownloaderProgressiveDownload = 1 << 1, // 帶有進(jìn)度
SDWebImageDownloaderUseNSURLCache = 1 << 2, // 使用URLCache
SDWebImageDownloaderIgnoreCachedResponse = 1 << 3, // 不緩存響應(yīng)
SDWebImageDownloaderContinueInBackground = 1 << 4, // 支持后臺(tái)下載
SDWebImageDownloaderHandleCookies = 1 << 5, // 使用Cookies
SDWebImageDownloaderAllowInvalidSSLCertificates = 1 << 6, // 允許驗(yàn)證SSL
SDWebImageDownloaderHighPriority = 1 << 7, // 高權(quán)限
SDWebImageDownloaderScaleDownLargeImages = 1 << 8, // 裁剪大圖片
};
這里提供了這么幾種不同的選項(xiàng)崔赌,大家可以根據(jù)自己的需求選個(gè)合適的選項(xiàng)。這里作者使用了掩碼滨嘱。比如說峰鄙,1 << 1 ,表示把1左移一位,我們把1攜程二進(jìn)制為:00000001太雨,那么左移一位后就是:00000010 轉(zhuǎn)成10進(jìn)制后就是2吟榴,也就是說左移一位表示在原來(lái)的值上乘以2。
再舉個(gè)例子囊扳,當(dāng)判斷self.option
是否是SDWebImageDownloaderIgnoreCachedResponse
選項(xiàng)時(shí)吩翻,應(yīng)該這么判斷:
self.option & SDWebImageDownloaderIgnoreCachedResponse
SDWebImageDownloaderExecutionOrder
SDWebImageDownloaderExecutionOrder
定義了數(shù)據(jù)被調(diào)用的順序。按照一般的想法锥咸。下載應(yīng)該按照數(shù)據(jù)放入隊(duì)列的順序依次進(jìn)行狭瞎,但也支持后進(jìn)先出這種方式。
一個(gè)下載管理器應(yīng)該這樣管理下載搏予,肯定有一個(gè)下載列表熊锭,我們可以假定這個(gè)列表保存在一個(gè)數(shù)組中,正常情況下我們應(yīng)該每次取出數(shù)組中第1個(gè)元素來(lái)下載,這就是FIFO(先進(jìn)先出)碗殷。那么我們要改為L(zhǎng)IFO(后進(jìn)先出)精绎,應(yīng)該是針對(duì)某一個(gè)下載的,不應(yīng)該是把取出數(shù)據(jù)的順序改為從數(shù)組的最后一個(gè)元素取出锌妻。
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
};
但是我有一個(gè)疑問代乃,如果我只是想暫停某一個(gè)下載,我該怎么辦呢仿粹?如果直接取消搁吓,那么點(diǎn)擊繼續(xù)的時(shí)候,要重新開啟下載吭历,肯定影響體驗(yàn)堕仔。這一點(diǎn)跟使用數(shù)組管理任務(wù)有點(diǎn)不同。
輔助部分
extern NSString * _Nonnull const SDWebImageDownloadStartNotification;
extern NSString * _Nonnull const SDWebImageDownloadStopNotification;
通過extern
,我們就能夠使用其他文件的代碼毒涧。
typedef void(^SDWebImageDownloaderProgressBlock)(NSInteger receivedSize, NSInteger expectedSize, NSURL * _Nullable targetURL);
typedef void(^SDWebImageDownloaderCompletedBlock)(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished);
命名Block
typedef NSDictionary<NSString *, NSString *> SDHTTPHeadersDictionary;
typedef NSMutableDictionary<NSString *, NSString *> SDHTTPHeadersMutableDictionary;
命名字典
typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterBlock)(NSURL * _Nullable url, SDHTTPHeadersDictionary * _Nullable headers);
這個(gè)block允許我們自定義請(qǐng)求頭贮预,通過Block傳值,有一定的優(yōu)點(diǎn)契讲,我們可以拿到一些參數(shù)仿吞,然后在加工成我們需要的數(shù)據(jù),最后返回捡偏。
SDWebImageDownloadToken
SDWebImageDownloadToken
作為每一個(gè)下載的唯一身份標(biāo)識(shí)唤冈,SDWebImageDownloader
和我們平時(shí)開發(fā)中的下載還是又不一樣的地方的,它弱化了下載過程银伟,比較強(qiáng)調(diào)的是下載結(jié)果你虹。不支持?jǐn)帱c(diǎn)下載(當(dāng)然這可能沒有必要)。
如果我們需要設(shè)計(jì)一個(gè)自己的下載管理者彤避,我們就應(yīng)該設(shè)計(jì)一個(gè)類似SDWebImageDownloadToken
這樣的下載對(duì)象封裝類傅物,我需要的所有信息,都能在這個(gè)對(duì)象中獲取琉预,大家如果有興趣董饰,可以看看MCDownloadManager.
/**
* A token associated with each download. Can be used to cancel a download
*/
@interface SDWebImageDownloadToken : NSObject
@property (nonatomic, strong, nullable) NSURL *url;
@property (nonatomic, strong, nullable) id downloadOperationCancelToken;
@end
SDWebImageDownloader.h
/**
* Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
* Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.
*/
@property (assign, nonatomic) BOOL shouldDecompressImages;
/**
* The maximum number of concurrent downloads
*/
@property (assign, nonatomic) NSInteger maxConcurrentDownloads;
/**
* Shows the current amount of downloads that still need to be downloaded
*/
@property (readonly, nonatomic) NSUInteger currentDownloadCount;
/**
* The timeout value (in seconds) for the download operation. Default: 15.0.
*/
@property (assign, nonatomic) NSTimeInterval downloadTimeout;
/**
* Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`.
*/
@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;
/**
* Singleton method, returns the shared instance
*
* @return global shared instance of downloader class
*/
+ (nonnull instancetype)sharedDownloader;
/**
* Set the default URL credential to be set for request operations.
*/
@property (strong, nonatomic, nullable) NSURLCredential *urlCredential;
/**
* Set username
*/
@property (strong, nonatomic, nullable) NSString *username;
/**
* Set password
*/
@property (strong, nonatomic, nullable) NSString *password;
/**
* Set filter to pick headers for downloading image HTTP request.
*
* This block will be invoked for each downloading image request, returned
* NSDictionary will be used as headers in corresponding HTTP request.
*/
@property (nonatomic, copy, nullable) SDWebImageDownloaderHeadersFilterBlock headersFilter;
初始化方法
/**
* Creates an instance of a downloader with specified session configuration.
* *Note*: `timeoutIntervalForRequest` is going to be overwritten.
* @return new instance of downloader class
*/
- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration NS_DESIGNATED_INITIALIZER;
我們都知道: 一個(gè)NSURLSession
會(huì)話,使用NSURLSessionConfiguration
進(jìn)行配置圆米,利用這一點(diǎn)卒暂,我們?cè)谠O(shè)計(jì)自己的網(wǎng)絡(luò)框架的時(shí)候,可以參考NSURLSessionConfiguration
娄帖。暴露少量的屬性來(lái)配置網(wǎng)絡(luò)請(qǐng)求也祠,后期維護(hù)起來(lái)也比較容易。
使用NS_DESIGNATED_INITIALIZER
強(qiáng)調(diào)該方法是建議的初始化方法近速。
其他的方法
/**
* Set a value for a HTTP header to be appended to each download HTTP request.
*
* @param value The value for the header field. Use `nil` value to remove the header.
* @param field The name of the header field to set.
*/
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field;
/**
* Returns the value of the specified HTTP header field.
*
* @return The value associated with the header field field, or `nil` if there is no corresponding header field.
*/
- (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field;
/**
* Sets a subclass of `SDWebImageDownloaderOperation` as the default
* `NSOperation` to be used each time SDWebImage constructs a request
* operation to download an image.
*
* @param operationClass The subclass of `SDWebImageDownloaderOperation` to set
* as default. Passing `nil` will revert to `SDWebImageDownloaderOperation`.
*/
- (void)setOperationClass:(nullable Class)operationClass;
/**
* Creates a SDWebImageDownloader async downloader instance with a given URL
*
* The delegate will be informed when the image is finish downloaded or an error has happen.
*
* @see SDWebImageDownloaderDelegate
*
* @param url The URL to the image to download
* @param options The options to be used for this download
* @param progressBlock A block called repeatedly while the image is downloading
* @note the progress block is executed on a background queue
* @param completedBlock A block called once the download is completed.
* If the download succeeded, the image parameter is set, in case of error,
* error parameter is set with the error. The last parameter is always YES
* if SDWebImageDownloaderProgressiveDownload isn't use. With the
* SDWebImageDownloaderProgressiveDownload option, this block is called
* repeatedly with the partial image object and the finished argument set to NO
* before to be called a last time with the full image and finished argument
* set to YES. In case of error, the finished argument is always YES.
*
* @return A token (SDWebImageDownloadToken) that can be passed to -cancel: to cancel this operation
*/
- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
options:(SDWebImageDownloaderOptions)options
progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;
/**
* Cancels a download that was previously queued using -downloadImageWithURL:options:progress:completed:
*
* @param token The token received from -downloadImageWithURL:options:progress:completed: that should be canceled.
*/
- (void)cancel:(nullable SDWebImageDownloadToken *)token;
/**
* Sets the download queue suspension state
*/
- (void)setSuspended:(BOOL)suspended;
/**
* Cancels all download operations in the queue
*/
- (void)cancelAllDownloads;
SDWebImageDownloader.m
@property (strong, nonatomic, nonnull) NSOperationQueue *downloadQueue;
@property (weak, nonatomic, nullable) NSOperation *lastAddedOperation;
@property (assign, nonatomic, nullable) Class operationClass;
@property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations;
@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;
這些屬性可以說都是為了完成管理下載任務(wù)而存在的诈嘿。
-
downloadQueue
隊(duì)列 -
lastAddedOperation
用于記錄最后添加的操作 -
operationClass
支持我們自定義的操作類 -
URLOperations
存放著所有的operation -
HTTPHeaders
HTTP請(qǐng)求頭 barrierQueue
session
initialize
initialize
和 load
這兩個(gè)方法比較特殊堪旧,我們通過下邊這個(gè)表格來(lái)看看他們的區(qū)別
.. | +(void)load | +(void)initialize |
---|---|---|
執(zhí)行時(shí)機(jī) | 在程序運(yùn)行后立即執(zhí)行 | 在類的方法第一次被調(diào)時(shí)執(zhí)行 |
若自身未定義,是否沿用父類的方法永淌? | 否 | 是 |
類別中的定義 | 全都執(zhí)行崎场,但后于類中的方法 | 覆蓋類中的方法,只執(zhí)行一個(gè) |
+ (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];
}
}
上邊的方法是為了給圖片下載綁定一個(gè)SDNetworkActivityIndicator
,只有當(dāng)這個(gè)SDNetworkActivityIndicator
文件存在的情況下才會(huì)執(zhí)行遂蛀,目的就是當(dāng)下在圖片是,狀態(tài)欄會(huì)轉(zhuǎn)小菊花干厚。
初始化實(shí)現(xiàn)
- (nonnull instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)sessionConfiguration {
if ((self = [super init])) {
_operationClass = [SDWebImageDownloaderOperation class];
_shouldDecompressImages = YES;
_executionOrder = SDWebImageDownloaderFIFOExecutionOrder;
_downloadQueue = [NSOperationQueue new];
_downloadQueue.maxConcurrentOperationCount = 6;
_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;
}
這里邊做了必要的初始化李滴,默認(rèn)最大支持的并發(fā)數(shù)為6個(gè),也就是說可以同時(shí)下載6張圖片蛮瞄。**我們看看image/webp,image/*;q=0.8
是什么意思所坯,image/webp
是web格式的圖片,q=0.8
指的是權(quán)重系數(shù)為0.8挂捅,q的取值范圍是0 - 1芹助, 默認(rèn)值為1,q作用于它前邊分號(hào);
前邊的內(nèi)容闲先。在這里状土,image/webp,image/*;q=0.8
表示優(yōu)先接受image/webp
,其次接受image/*
的圖片。
set或者get方法
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(nullable NSString *)field {
if (value) {
self.HTTPHeaders[field] = value;
}
else {
[self.HTTPHeaders removeObjectForKey:field];
}
}
- (nullable NSString *)valueForHTTPHeaderField:(nullable NSString *)field {
return self.HTTPHeaders[field];
}
- (void)setMaxConcurrentDownloads:(NSInteger)maxConcurrentDownloads {
_downloadQueue.maxConcurrentOperationCount = maxConcurrentDownloads;
}
- (NSUInteger)currentDownloadCount {
return _downloadQueue.operationCount;
}
- (NSInteger)maxConcurrentDownloads {
return _downloadQueue.maxConcurrentOperationCount;
}
- (void)setOperationClass:(nullable Class)operationClass {
if (operationClass && [operationClass isSubclassOfClass:[NSOperation class]] && [operationClass conformsToProtocol:@protocol(SDWebImageDownloaderOperationInterface)]) {
_operationClass = operationClass;
} else {
_operationClass = [SDWebImageDownloaderOperation class];
}
}
為每個(gè)URL綁定事件
- (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.
if (url == nil) {
if (completedBlock != nil) {
completedBlock(nil, nil, nil, NO);
}
return nil;
}
__block SDWebImageDownloadToken *token = nil;
dispatch_barrier_sync(self.barrierQueue, ^{
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
if (!operation) {
operation = createCallback();
self.URLOperations[url] = operation;
__weak SDWebImageDownloaderOperation *woperation = 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;
}
每一個(gè)URL都會(huì)被作為每個(gè)下載的唯一標(biāo)識(shí)伺糠,每一個(gè)下載都會(huì)綁定一個(gè)progressBlock和completeBlock蒙谓,最終我們還要使用這個(gè)URL跟NSOperation
建立聯(lián)系。
關(guān)于如何創(chuàng)建SDWebImageDownloaderOperation
這一段代碼训桶,我采用注釋的方式累驮,大家看注釋就行了。
- (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;
// 1.設(shè)置超時(shí)時(shí)間
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
// 2.創(chuàng)建request舵揭,注意我們?cè)O(shè)置的緩存策略的選擇
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:(options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:timeoutInterval];
request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
request.HTTPShouldUsePipelining = YES;
// 3.設(shè)置請(qǐng)求頭部
if (sself.headersFilter) {
request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
}
else {
request.allHTTPHeaderFields = sself.HTTPHeaders;
}
// 4.創(chuàng)建操作對(duì)象
SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
operation.shouldDecompressImages = sself.shouldDecompressImages;
// 5.給操作對(duì)象設(shè)置urlCredential
if (sself.urlCredential) {
operation.credential = sself.urlCredential;
} else if (sself.username && sself.password) {
operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
}
// 6.設(shè)置操作級(jí)別
if (options & SDWebImageDownloaderHighPriority) {
operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) {
operation.queuePriority = NSOperationQueuePriorityLow;
}
// 7.把操作添加到隊(duì)列
[sself.downloadQueue addOperation:operation];
// 8.根據(jù)executionOrder設(shè)置谤专,設(shè)置依賴
if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
// Emulate LIFO execution order by systematically adding new operations as last operation's dependency
[sself.lastAddedOperation addDependency:operation];
sself.lastAddedOperation = operation;
}
return operation;
}];
}
取消摸個(gè)操作
- (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];
}
});
}
全部暫停或取消
- (void)setSuspended:(BOOL)suspended {
(self.downloadQueue).suspended = suspended;
}
- (void)cancelAllDownloads {
[self.downloadQueue cancelAllOperations];
}
會(huì)話的代理
我們?cè)诔跏蓟椒ㄖ袆?chuàng)建了會(huì)話:
self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration
delegate:self
delegateQueue:nil];
delegate設(shè)置為自己午绳,也就是當(dāng)使用這個(gè)會(huì)話請(qǐng)求數(shù)據(jù)置侍,收到響應(yīng)時(shí),會(huì)調(diào)用SDWebImageDownloader.m
中的代理方法箱叁,然后再調(diào)用SDWebImageDownloaderOperation
中的代理方法處理事情墅垮。
那么作者為什么這么設(shè)計(jì)呢?目的就是為了共用一個(gè)URLSession耕漱。
- (SDWebImageDownloaderOperation *)operationWithTask:(NSURLSessionTask *)task {
SDWebImageDownloaderOperation *returnOperation = nil;
for (SDWebImageDownloaderOperation *operation in self.downloadQueue.operations) {
if (operation.dataTask.taskIdentifier == task.taskIdentifier) {
returnOperation = operation;
break;
}
}
return returnOperation;
}
#pragma mark NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
// Identify the operation that runs this task and pass it the delegate method
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
[dataOperation URLSession:session dataTask:dataTask didReceiveResponse:response completionHandler:completionHandler];
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
// Identify the operation that runs this task and pass it the delegate method
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
[dataOperation URLSession:session dataTask:dataTask didReceiveData:data];
}
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
// Identify the operation that runs this task and pass it the delegate method
SDWebImageDownloaderOperation *dataOperation = [self operationWithTask:dataTask];
[dataOperation URLSession:session dataTask:dataTask willCacheResponse:proposedResponse completionHandler:completionHandler];
}
#pragma mark 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];
}
由于個(gè)人知識(shí)有限算色,如有錯(cuò)誤之處,還望各路大俠給予指出啊
- SDWebImage源碼解讀 之 NSData+ImageContentType 簡(jiǎn)書 博客園
- SDWebImage源碼解讀 之 UIImage+GIF 簡(jiǎn)書 博客園
- SDWebImage源碼解讀 之 SDWebImageCompat 簡(jiǎn)書 博客園
- SDWebImage源碼解讀 之SDWebImageDecoder 簡(jiǎn)書 博客園
- SDWebImage源碼解讀 之SDWebImageCache(上) 簡(jiǎn)書 博客園
- SDWebImage源碼解讀之SDWebImageCache(下) 簡(jiǎn)書 博客園
- SDWebImage源碼解讀之SDWebImageDownloaderOperation 簡(jiǎn)書 博客園