版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2018.03.04 |
前言
我們做APP發(fā)起網(wǎng)絡(luò)請(qǐng)求,都離不開(kāi)一個(gè)非常有用的框架AFNetworking袜硫,可以說(shuō)這個(gè)框架的知名度已經(jīng)超過(guò)了蘋(píng)果的底層網(wǎng)絡(luò)請(qǐng)求部分留荔,很多人可能不知道蘋(píng)果底層是如何發(fā)起網(wǎng)絡(luò)請(qǐng)求的府树,但是一定知道
AFNetworking
,接下來(lái)幾篇我們就一起詳細(xì)的解析一下這個(gè)框架偎蘸。感興趣的可以看上面寫(xiě)的幾篇庄蹋。
1. AFNetworking源碼探究(一) —— 基本介紹
2. AFNetworking源碼探究(二) —— GET請(qǐng)求實(shí)現(xiàn)之NSURLSessionDataTask實(shí)例化(一)
3. AFNetworking源碼探究(三) —— GET請(qǐng)求實(shí)現(xiàn)之任務(wù)進(jìn)度設(shè)置和通知監(jiān)聽(tīng)(一)
4. AFNetworking源碼探究(四) —— GET請(qǐng)求實(shí)現(xiàn)之代理轉(zhuǎn)發(fā)思想(一)
5. AFNetworking源碼探究(五) —— AFURLSessionManager中NSURLSessionDelegate詳細(xì)解析(一)
6. AFNetworking源碼探究(六) —— AFURLSessionManager中NSURLSessionTaskDelegate詳細(xì)解析(一)
7. AFNetworking源碼探究(七) —— AFURLSessionManager中NSURLSessionDataDelegate詳細(xì)解析(一)
8. AFNetworking源碼探究(八) —— AFURLSessionManager中NSURLSessionDownloadDelegate詳細(xì)解析(一)
9. AFNetworking源碼探究(九) —— AFURLSessionManagerTaskDelegate中三個(gè)轉(zhuǎn)發(fā)代理方法詳細(xì)解析(一)
10. AFNetworking源碼探究(十) —— 數(shù)據(jù)解析之?dāng)?shù)據(jù)解析架構(gòu)的分析(一)
11. AFNetworking源碼探究(十一) —— 數(shù)據(jù)解析之子類(lèi)中協(xié)議方法的實(shí)現(xiàn)(二)
12. AFNetworking源碼探究(十二) —— 數(shù)據(jù)解析之子類(lèi)中協(xié)議方法的實(shí)現(xiàn)(三)
13. AFNetworking源碼探究(十三) —— AFSecurityPolicy與安全認(rèn)證 (一)
14. AFNetworking源碼探究(十四) —— AFSecurityPolicy與安全認(rèn)證 (二)
15. AFNetworking源碼探究(十五) —— 請(qǐng)求序列化之架構(gòu)分析(一)
16. AFNetworking源碼探究(十六) —— 請(qǐng)求序列化之協(xié)議方法的實(shí)現(xiàn)(二)
17. AFNetworking源碼探究(十七) —— _AFURLSessionTaskSwizzling實(shí)現(xiàn)方法交換(轉(zhuǎn)載)(一)
18. AFNetworking源碼探究(十八) —— UIKit相關(guān)之AFNetworkActivityIndicatorManager(一)
19. AFNetworking源碼探究(十九) —— UIKit相關(guān)之幾個(gè)分類(lèi)(二)
回顧
上一篇主要介紹了AFNetworkActivityIndicatorManager
這個(gè)與UIKit相關(guān)的類(lèi),這一篇主要介紹AFImageDownloader
有關(guān)圖像的下載迷雪。
AFImageDownloader文件
我們先看一下這個(gè)文件限书,這個(gè)文件包含兩個(gè)類(lèi)。分別為AFImageDownloadReceipt
和AFImageDownloader
章咧。
1. AFImageDownloader
先看一下AFImageDownloader.h
的接口
1. AFImageDownloader.h
/** The `AFImageDownloader` class is responsible for downloading images in parallel on a prioritized queue. Incoming downloads are added to the front or back of the queue depending on the download prioritization. Each downloaded image is cached in the underlying `NSURLCache` as well as the in-memory image cache. By default, any download request with a cached image equivalent in the image cache will automatically be served the cached image representation.
*/
@interface AFImageDownloader : NSObject
/**
The image cache used to store all downloaded images in. `AFAutoPurgingImageCache` by default.
*/
// 圖像緩存默認(rèn)情況下用于將所有下載的圖像存儲(chǔ)在AFAutoPurgingImageCache中倦西。
@property (nonatomic, strong, nullable) id <AFImageRequestCache> imageCache;
/**
The `AFHTTPSessionManager` used to download images. By default, this is configured with an `AFImageResponseSerializer`, and a shared `NSURLCache` for all image downloads.
*/
// 用于下載圖像的AFHTTPSessionManager。 默認(rèn)情況下赁严,
// 這是通過(guò)一個(gè)AFImageResponseSerializer和一個(gè)共享的NSURLCache來(lái)配置所有的圖片下載扰柠。
@property (nonatomic, strong) AFHTTPSessionManager *sessionManager;
/**
Defines the order prioritization of incoming download requests being inserted into the queue. `AFImageDownloadPrioritizationFIFO` by default.
*/
// 定義插入隊(duì)列的傳入下載請(qǐng)求的順序優(yōu)先級(jí)。 默認(rèn)情況下為AFImageDownloadPrioritizationFIFO疼约。
@property (nonatomic, assign) AFImageDownloadPrioritization downloadPrioritizaton;
/**
The shared default instance of `AFImageDownloader` initialized with default values.
*/
// AFImageDownloader的共享默認(rèn)實(shí)例使用默認(rèn)值初始化卤档。
+ (instancetype)defaultInstance;
/**
Creates a default `NSURLCache` with common usage parameter values.
@returns The default `NSURLCache` instance.
*/
// 使用常用的使用參數(shù)值創(chuàng)建一個(gè)默認(rèn)的NSURLCache
+ (NSURLCache *)defaultURLCache;
/**
The default `NSURLSessionConfiguration` with common usage parameter values.
*/
// 默認(rèn)的NSURLSessionConfiguration具有常用的使用參數(shù)值。
+ (NSURLSessionConfiguration *)defaultURLSessionConfiguration;
/**
Default initializer
@return An instance of `AFImageDownloader` initialized with default values.
*/
- (instancetype)init;
/**
Initializer with specific `URLSessionConfiguration`
@param configuration The `NSURLSessionConfiguration` to be be used
@return An instance of `AFImageDownloader` initialized with default values and custom `NSURLSessionConfiguration`
*/
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration;
/**
Initializes the `AFImageDownloader` instance with the given session manager, download prioritization, maximum active download count and image cache.
// 使用給定的會(huì)話(huà)管理器程剥、下載優(yōu)先級(jí)劝枣,最大活動(dòng)下載計(jì)數(shù)和圖像緩存
// 來(lái)初始化AFImageDownloader實(shí)例。
@param sessionManager The session manager to use to download images.
@param downloadPrioritization The download prioritization of the download queue.
@param maximumActiveDownloads The maximum number of active downloads allowed at any given time. Recommend `4`.
@param imageCache The image cache used to store all downloaded images in.
@return The new `AFImageDownloader` instance.
*/
- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager
downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization
maximumActiveDownloads:(NSInteger)maximumActiveDownloads
imageCache:(nullable id <AFImageRequestCache>)imageCache;
/**
Creates a data task using the `sessionManager` instance for the specified URL request.
If the same data task is already in the queue or currently being downloaded, the success and failure blocks are
appended to the already existing task. Once the task completes, all success or failure blocks attached to the
task are executed in the order they were added.
// 使用sessionManager實(shí)例為指定的URL請(qǐng)求創(chuàng)建一個(gè)數(shù)據(jù)任務(wù)。
如果相同的數(shù)據(jù)任務(wù)已經(jīng)在隊(duì)列中或當(dāng)前正在下載舔腾,則成功和失敗模塊是
附加到已經(jīng)存在的任務(wù)溪胶。 一旦任務(wù)完成,所有成功或失敗塊附加到
任務(wù)并按照它們添加的順序執(zhí)行琢唾。
@param request The URL request.
@param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`.
// 當(dāng)圖像數(shù)據(jù)任務(wù)成功完成時(shí)要執(zhí)行的塊。 該塊沒(méi)有返回值盾饮,并且有三個(gè)參數(shù):
// 客戶(hù)端發(fā)送的請(qǐng)求采桃,從服務(wù)器收到的響應(yīng)以及從請(qǐng)求響應(yīng)數(shù)據(jù)創(chuàng)建的圖像。
// 如果圖像是從緩存中返回的丘损,則響應(yīng)參數(shù)將為nil普办。
@param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
// 當(dāng)圖像數(shù)據(jù)任務(wù)完成失敗或成功完成時(shí)要執(zhí)行的塊對(duì)象。
// 該塊沒(méi)有返回值徘钥,并且有三個(gè)參數(shù):客戶(hù)端發(fā)送的請(qǐng)求衔蹲,
// 從服務(wù)器接收到的響應(yīng)以及描述發(fā)生的網(wǎng)絡(luò)或解析錯(cuò)誤的錯(cuò)誤對(duì)象。
@return The image download receipt for the data task if available. `nil` if the image is stored in the cache.
cache and the URL request cache policy allows the cache to be used.
// 數(shù)據(jù)任務(wù)的圖像下載收據(jù)(如果存在)呈础。 如果圖像存儲(chǔ)在緩存中舆驶,則為nil。 緩存和URL請(qǐng)求緩存策略允許使用緩存而钞。
*/
- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
/**
Creates a data task using the `sessionManager` instance for the specified URL request.
If the same data task is already in the queue or currently being downloaded, the success and failure blocks are
appended to the already existing task. Once the task completes, all success or failure blocks attached to the
task are executed in the order they were added.
// 使用sessionManager實(shí)例為指定的URL請(qǐng)求創(chuàng)建一個(gè)數(shù)據(jù)任務(wù)沙廉。
如果相同的數(shù)據(jù)任務(wù)已經(jīng)在隊(duì)列中或當(dāng)前正在下載,則成功和失敗block
附加到已經(jīng)存在的任務(wù)臼节。 一旦任務(wù)完成撬陵,所有成功或失敗block附加到
任務(wù)并按照它們添加的順序執(zhí)行。
@param request The URL request.
@param receiptID The identifier to use for the download receipt that will be created for this request. This must be a unique identifier that does not represent any other request.
// 用于為此請(qǐng)求創(chuàng)建的下載收據(jù)的標(biāo)識(shí)符网缝。 這必須是不代表任何其他請(qǐng)求的唯一標(biāo)識(shí)符巨税。
@param success A block to be executed when the image data task finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the response parameter will be `nil`.
@param failure A block object to be executed when the image data task finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
@return The image download receipt for the data task if available. `nil` if the image is stored in the cache.
cache and the URL request cache policy allows the cache to be used.
*/
- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
withReceiptID:(NSUUID *)receiptID
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
/**
Cancels the data task in the receipt by removing the corresponding success and failure blocks and cancelling the data task if necessary.
If the data task is pending in the queue, it will be cancelled if no other success and failure blocks are registered with the data task. If the data task is currently executing or is already completed, the success and failure blocks are removed and will not be called when the task finishes.
// 通過(guò)刪除相應(yīng)的成功和失敗塊并在必要時(shí)取消數(shù)據(jù)任務(wù)來(lái)取消收據(jù)中的數(shù)據(jù)任務(wù)。
如果數(shù)據(jù)任務(wù)在隊(duì)列中待處理粉臊,如果沒(méi)有其他成功和失敗塊向數(shù)據(jù)任務(wù)注冊(cè)草添,
則它將被取消。 如果數(shù)據(jù)任務(wù)當(dāng)前正在執(zhí)行或已經(jīng)完成扼仲,則成功和失敗塊將被刪除果元,
并且在任務(wù)完成時(shí)不會(huì)被調(diào)用。
@param imageDownloadReceipt The image download receipt to cancel.
*/
- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt;
@end
AFImageDownloader
類(lèi)負(fù)責(zé)在優(yōu)先隊(duì)列中并行下載圖像犀盟。 根據(jù)下載優(yōu)先級(jí)而晒,傳入的下載將被添加到隊(duì)列的前面或后面。 每個(gè)下載的圖像都緩存在底層的NSURLCache
以及內(nèi)存中的圖像緩存中阅畴。 默認(rèn)情況下倡怎,任何具有圖像緩存中等效緩存圖像的下載請(qǐng)求都將自動(dòng)提供緩存圖像表示。
2. AFImageDownloadReceipt
先看一個(gè)AFImageDownloadReceipt.h
中的接口。
typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
AFImageDownloadPrioritizationFIFO,
AFImageDownloadPrioritizationLIFO
};
/**
The `AFImageDownloadReceipt` is an object vended by the `AFImageDownloader` when starting a data task. It can be used to cancel active tasks running on the `AFImageDownloader` session. As a general rule, image data tasks should be cancelled using the `AFImageDownloadReceipt` instead of calling `cancel` directly on the `task` itself. The `AFImageDownloader` is optimized to handle duplicate task scenarios as well as pending versus active downloads.
*/
@interface AFImageDownloadReceipt : NSObject
/**
The data task created by the `AFImageDownloader`.
*/
@property (nonatomic, strong) NSURLSessionDataTask *task;
/**
The unique identifier for the success and failure blocks when duplicate requests are made.
*/
@property (nonatomic, strong) NSUUID *receiptID;
@end
AFImageDownloadReceipt
是啟動(dòng)數(shù)據(jù)任務(wù)時(shí)由AFImageDownloader
提供的對(duì)象监署。 它可以用來(lái)取消在AFImageDownloader
會(huì)話(huà)中運(yùn)行的活動(dòng)任務(wù)颤专。 作為一般規(guī)則栖榨,應(yīng)該使用AFImageDownloadReceipt
來(lái)取消圖像數(shù)據(jù)任務(wù)炬转,而不是直接在task
本身上調(diào)用cancel
矛市。 AFImageDownloader
經(jīng)過(guò)優(yōu)化啃憎,可以處理重復(fù)的任務(wù)場(chǎng)景以及待處理和活動(dòng)下載捅彻。
AFImageDownloader初始化
下面我們先看一下AFImageDownloader
的初始化业踏。
它的初始化就是一個(gè)單例
+ (instancetype)defaultInstance {
static AFImageDownloader *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
然后重寫(xiě)了下init
方法划滋。
- (instancetype)init {
// 調(diào)用defaultURLSessionConfiguration類(lèi)方法進(jìn)行配置
NSURLSessionConfiguration *defaultConfiguration = [self.class defaultURLSessionConfiguration];
return [self initWithSessionConfiguration:defaultConfiguration];
}
+ (NSURLSessionConfiguration *)defaultURLSessionConfiguration {
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
//TODO set the default HTTP headers
configuration.HTTPShouldSetCookies = YES;
configuration.HTTPShouldUsePipelining = NO;
configuration.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
configuration.allowsCellularAccess = YES;
configuration.timeoutIntervalForRequest = 60.0;
configuration.URLCache = [AFImageDownloader defaultURLCache];
return configuration;
}
大家看一下這里都是常規(guī)的配置杜漠。
然后就是直接調(diào)用下面的方法進(jìn)行初始化俏拱。
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
sessionManager.responseSerializer = [AFImageResponseSerializer serializer];
return [self initWithSessionManager:sessionManager
downloadPrioritization:AFImageDownloadPrioritizationFIFO
maximumActiveDownloads:4
imageCache:[[AFAutoPurgingImageCache alloc] init]];
}
- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager
downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization
maximumActiveDownloads:(NSInteger)maximumActiveDownloads
imageCache:(id <AFImageRequestCache>)imageCache {
if (self = [super init]) {
self.sessionManager = sessionManager;
self.downloadPrioritizaton = downloadPrioritization;
self.maximumActiveDownloads = maximumActiveDownloads;
self.imageCache = imageCache;
self.queuedMergedTasks = [[NSMutableArray alloc] init];
self.mergedTasks = [[NSMutableDictionary alloc] init];
self.activeRequestCount = 0;
// 串行隊(duì)列暑塑,[[NSUUID UUID] UUIDString]保證唯一性
NSString *name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.synchronizationqueue-%@", [[NSUUID UUID] UUIDString]];
self.synchronizationQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);
// 并行隊(duì)列
name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.responsequeue-%@", [[NSUUID UUID] UUIDString]];
self.responseQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
這里實(shí)例化了兩個(gè)隊(duì)列,分別是串行隊(duì)列锅必,另外一個(gè)是并行隊(duì)列事格。隊(duì)列名字包含了字符串[[NSUUID UUID] UUIDString]
,保證了唯一性搞隐。
圖像的下載
下面我們就看一下圖像的下載過(guò)程驹愚。
- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
withReceiptID:(NSUUID *)receiptID
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure;
一共就上面兩個(gè)下載方法。
- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
success:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, UIImage * _Nonnull))success
failure:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, NSError * _Nonnull))failure {
return [self downloadImageForURLRequest:request withReceiptID:[NSUUID UUID] success:success failure:failure];
}
這個(gè)方法實(shí)現(xiàn)上直接調(diào)用第二個(gè)方法劣纲,默認(rèn)的ReceiptID參數(shù)傳遞[NSUUID UUID]
么鹤。
- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
withReceiptID:(nonnull NSUUID *)receiptID
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure {
// 這里是在串行隊(duì)列上進(jìn)行處理,隊(duì)列已經(jīng)進(jìn)行了初始化
__block NSURLSessionDataTask *task = nil;
dispatch_sync(self.synchronizationQueue, ^{
NSString *URLIdentifier = request.URL.absoluteString;
if (URLIdentifier == nil) {
if (failure) {
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil];
// 如果request.URL = nil味廊,就在主線(xiàn)程回調(diào) failure
dispatch_async(dispatch_get_main_queue(), ^{
failure(request, nil, error);
});
}
return;
}
// 1) Append the success and failure blocks to a pre-existing request if it already exists
AFImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[URLIdentifier];
if (existingMergedTask != nil) {
AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure];
[existingMergedTask addResponseHandler:handler];
task = existingMergedTask.task;
return;
}
// 2) Attempt to load the image from the image cache if the cache policy allows it
switch (request.cachePolicy) {
case NSURLRequestUseProtocolCachePolicy:
case NSURLRequestReturnCacheDataElseLoad:
case NSURLRequestReturnCacheDataDontLoad: {
UIImage *cachedImage = [self.imageCache imageforRequest:request withAdditionalIdentifier:nil];
if (cachedImage != nil) {
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
success(request, nil, cachedImage);
});
}
return;
}
break;
}
default:
break;
}
// 3) Create the request and set up authentication, validation and response serialization
NSUUID *mergedTaskIdentifier = [NSUUID UUID];
NSURLSessionDataTask *createdTask;
__weak __typeof__(self) weakSelf = self;
createdTask = [self.sessionManager
dataTaskWithRequest:request
uploadProgress:nil
downloadProgress:nil
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
dispatch_async(self.responseQueue, ^{
__strong __typeof__(weakSelf) strongSelf = weakSelf;
AFImageDownloaderMergedTask *mergedTask = strongSelf.mergedTasks[URLIdentifier];
if ([mergedTask.identifier isEqual:mergedTaskIdentifier]) {
mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier];
if (error) {
for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
if (handler.failureBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
handler.failureBlock(request, (NSHTTPURLResponse*)response, error);
});
}
}
} else {
if ([strongSelf.imageCache shouldCacheImage:responseObject forRequest:request withAdditionalIdentifier:nil]) {
[strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil];
}
for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
if (handler.successBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
handler.successBlock(request, (NSHTTPURLResponse*)response, responseObject);
});
}
}
}
}
[strongSelf safelyDecrementActiveTaskCount];
[strongSelf safelyStartNextTaskIfNecessary];
});
}];
// 4) Store the response handler for use when the request completes
AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID
success:success
failure:failure];
AFImageDownloaderMergedTask *mergedTask = [[AFImageDownloaderMergedTask alloc]
initWithURLIdentifier:URLIdentifier
identifier:mergedTaskIdentifier
task:createdTask];
[mergedTask addResponseHandler:handler];
self.mergedTasks[URLIdentifier] = mergedTask;
// 5) Either start the request or enqueue it depending on the current active request count
if ([self isActiveRequestCountBelowMaximumLimit]) {
[self startMergedTask:mergedTask];
} else {
[self enqueueMergedTask:mergedTask];
}
task = mergedTask.task;
});
if (task) {
return [[AFImageDownloadReceipt alloc] initWithReceiptID:receiptID task:task];
} else {
return nil;
}
}
主要做了下面幾個(gè)工作:
1. 向預(yù)先存在請(qǐng)求中添加成功失敗回調(diào)塊
如果成功和失敗塊已經(jīng)存在蒸甜,則將其添加到預(yù)先存在的請(qǐng)求中,主要對(duì)應(yīng)下面這段代碼余佛。
AFImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[URLIdentifier];
if (existingMergedTask != nil) {
AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure];
[existingMergedTask addResponseHandler:handler];
task = existingMergedTask.task;
return;
}
這里AFImageDownloaderMergedTask
是一個(gè)新的類(lèi)柠新,先看一下API文檔。
@interface AFImageDownloaderMergedTask : NSObject
@property (nonatomic, strong) NSString *URLIdentifier;
@property (nonatomic, strong) NSUUID *identifier;
@property (nonatomic, strong) NSURLSessionDataTask *task;
@property (nonatomic, strong) NSMutableArray <AFImageDownloaderResponseHandler*> *responseHandlers;
@end
@implementation AFImageDownloaderMergedTask
// 初始化對(duì)象
- (instancetype)initWithURLIdentifier:(NSString *)URLIdentifier identifier:(NSUUID *)identifier task:(NSURLSessionDataTask *)task {
if (self = [self init]) {
self.URLIdentifier = URLIdentifier;
self.task = task;
self.identifier = identifier;
self.responseHandlers = [[NSMutableArray alloc] init];
}
return self;
}
// 數(shù)組添加對(duì)象
- (void)addResponseHandler:(AFImageDownloaderResponseHandler*)handler {
[self.responseHandlers addObject:handler];
}
// 數(shù)組移除對(duì)象
- (void)removeResponseHandler:(AFImageDownloaderResponseHandler*)handler {
[self.responseHandlers removeObject:handler];
}
@end
這里有一個(gè)數(shù)組responseHandlers
里面存放的隊(duì)形類(lèi)型就是AFImageDownloaderResponseHandler
辉巡。
@property (nonatomic, strong) NSMutableArray <AFImageDownloaderResponseHandler*> *responseHandlers;
@interface AFImageDownloaderResponseHandler : NSObject
@property (nonatomic, strong) NSUUID *uuid;
@property (nonatomic, copy) void (^successBlock)(NSURLRequest*, NSHTTPURLResponse*, UIImage*);
@property (nonatomic, copy) void (^failureBlock)(NSURLRequest*, NSHTTPURLResponse*, NSError*);
@end
@implementation AFImageDownloaderResponseHandler
- (instancetype)initWithUUID:(NSUUID *)uuid
success:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *responseObject))success
failure:(nullable void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure {
if (self = [self init]) {
self.uuid = uuid;
self.successBlock = success;
self.failureBlock = failure;
}
return self;
}
- (NSString *)description {
return [NSString stringWithFormat: @"<AFImageDownloaderResponseHandler>UUID: %@", [self.uuid UUIDString]];
}
@end
這個(gè)AFImageDownloaderResponseHandler
對(duì)象將UUID恨憎、成功回調(diào)和失敗回調(diào)作為屬性包了進(jìn)去,方便使用郊楣。
下面我們還是回來(lái)到下載圖像的源代碼中憔恳。
這里有一個(gè)字典,URLIdentifier作為key净蚤,取出來(lái)的就是AFImageDownloaderMergedTask
對(duì)象钥组。
@property (nonatomic, strong) NSMutableDictionary *mergedTasks;
如果請(qǐng)求任務(wù)已經(jīng)存在,那么就實(shí)例化AFImageDownloaderResponseHandler對(duì)象并添加到數(shù)組中今瀑。并且進(jìn)行賦值task = existingMergedTask.task
程梦,取出來(lái)存在的task傳給自定義的task對(duì)象点把。最后return返回。
2. 從緩存中添加圖像
switch (request.cachePolicy) {
case NSURLRequestUseProtocolCachePolicy:
case NSURLRequestReturnCacheDataElseLoad:
case NSURLRequestReturnCacheDataDontLoad: {
UIImage *cachedImage = [self.imageCache imageforRequest:request withAdditionalIdentifier:nil];
if (cachedImage != nil) {
if (success) {
dispatch_async(dispatch_get_main_queue(), ^{
success(request, nil, cachedImage);
});
}
return;
}
break;
}
default:
break;
}
這段代碼還是好理解吧屿附,利用下面方法取出緩存圖像郎逃,并且在NSURLRequestUseProtocolCachePolicy
、 NSURLRequestReturnCacheDataElseLoad
和NSURLRequestReturnCacheDataDontLoad
情況下挺份,取出來(lái)圖像褒翰,如果不為空就調(diào)用success的回調(diào)。
- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
3. 創(chuàng)建請(qǐng)求匀泊,設(shè)置權(quán)限驗(yàn)證和響應(yīng)序列化
NSUUID *mergedTaskIdentifier = [NSUUID UUID];
NSURLSessionDataTask *createdTask;
__weak __typeof__(self) weakSelf = self;
createdTask = [self.sessionManager
dataTaskWithRequest:request
uploadProgress:nil
downloadProgress:nil
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
dispatch_async(self.responseQueue, ^{
__strong __typeof__(weakSelf) strongSelf = weakSelf;
AFImageDownloaderMergedTask *mergedTask = strongSelf.mergedTasks[URLIdentifier];
if ([mergedTask.identifier isEqual:mergedTaskIdentifier]) {
mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier];
if (error) {
for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
if (handler.failureBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
handler.failureBlock(request, (NSHTTPURLResponse*)response, error);
});
}
}
} else {
if ([strongSelf.imageCache shouldCacheImage:responseObject forRequest:request withAdditionalIdentifier:nil]) {
[strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil];
}
for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
if (handler.successBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
handler.successBlock(request, (NSHTTPURLResponse*)response, responseObject);
});
}
}
}
}
[strongSelf safelyDecrementActiveTaskCount];
[strongSelf safelyStartNextTaskIfNecessary];
});
}];
調(diào)用AFHTTPSessionManager
類(lèi)中的方法返回NSURLSessionDataTask *createdTask
對(duì)象优训。
這里uploadProgress
和downloadProgress
都傳遞為nil,并且在回調(diào)完成的方法中探赫,生成異步并行隊(duì)列進(jìn)行處理型宙。
這里首先取出對(duì)象AFImageDownloaderMergedTask *mergedTask
撬呢,然后根據(jù)[mergedTask.identifier isEqual:mergedTaskIdentifier]
進(jìn)行比較伦吠,滿(mǎn)足了以后調(diào)用下面方法返回mergedTask對(duì)象,并將其對(duì)應(yīng)的key從字典中移除魂拦。
- (AFImageDownloaderMergedTask*)safelyRemoveMergedTaskWithURLIdentifier:(NSString *)URLIdentifier {
__block AFImageDownloaderMergedTask *mergedTask = nil;
dispatch_sync(self.synchronizationQueue, ^{
mergedTask = [self removeMergedTaskWithURLIdentifier:URLIdentifier];
});
return mergedTask;
}
//This method should only be called from safely within the synchronizationQueue
- (AFImageDownloaderMergedTask *)removeMergedTaskWithURLIdentifier:(NSString *)URLIdentifier {
AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier];
[self.mergedTasks removeObjectForKey:URLIdentifier];
return mergedTask;
}
接著就是根據(jù)回調(diào)error參數(shù)毛仪,進(jìn)行判斷
如果error不為空,也就是存在錯(cuò)誤
if (error) {
for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
if (handler.failureBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
handler.failureBlock(request, (NSHTTPURLResponse*)response, error);
});
}
}
}
那么就遍歷responseHandlers
數(shù)組芯勘,找到其對(duì)應(yīng)的failureBlock
屬性箱靴,并在主線(xiàn)程回調(diào)block。
如果error為nil
else {
if ([strongSelf.imageCache shouldCacheImage:responseObject forRequest:request withAdditionalIdentifier:nil]) {
[strongSelf.imageCache addImage:responseObject forRequest:request withAdditionalIdentifier:nil];
}
for (AFImageDownloaderResponseHandler *handler in mergedTask.responseHandlers) {
if (handler.successBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
handler.successBlock(request, (NSHTTPURLResponse*)response, responseObject);
});
}
}
}
在這里接著進(jìn)行了判斷荷愕,如果需要緩存圖像衡怀,那么就調(diào)用方法進(jìn)行緩存;遍歷responseHandlers
數(shù)組安疗,找到其對(duì)應(yīng)的successBlock
屬性抛杨,并在主線(xiàn)程回調(diào)block。
接著就是
// 減小任務(wù)計(jì)數(shù)器的計(jì)數(shù)值
[strongSelf safelyDecrementActiveTaskCount];
// 如果需要的話(huà)開(kāi)啟下一個(gè)任務(wù)
[strongSelf safelyStartNextTaskIfNecessary];
// 減小任務(wù)計(jì)數(shù)
- (void)safelyDecrementActiveTaskCount {
dispatch_sync(self.synchronizationQueue, ^{
if (self.activeRequestCount > 0) {
self.activeRequestCount -= 1;
}
});
}
// 根據(jù)需要增加任務(wù)計(jì)數(shù)
- (void)safelyStartNextTaskIfNecessary {
dispatch_sync(self.synchronizationQueue, ^{
if ([self isActiveRequestCountBelowMaximumLimit]) {
while (self.queuedMergedTasks.count > 0) {
AFImageDownloaderMergedTask *mergedTask = [self dequeueMergedTask];
if (mergedTask.task.state == NSURLSessionTaskStateSuspended) {
[self startMergedTask:mergedTask];
break;
}
}
}
});
}
- (void)startMergedTask:(AFImageDownloaderMergedTask *)mergedTask {
[mergedTask.task resume];
++self.activeRequestCount;
}
4. 請(qǐng)求完成時(shí)存儲(chǔ)響應(yīng)處理程序以備使用
主要對(duì)應(yīng)下面這段代碼
// AFImageDownloaderResponseHandler實(shí)例化
AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID
success:success
failure:failure];
// AFImageDownloaderMergedTask實(shí)例化
AFImageDownloaderMergedTask *mergedTask = [[AFImageDownloaderMergedTask alloc]
initWithURLIdentifier:URLIdentifier
identifier:mergedTaskIdentifier
task:createdTask];
// 向數(shù)組中添加響應(yīng)
[mergedTask addResponseHandler:handler];
self.mergedTasks[URLIdentifier] = mergedTask;
5. 根據(jù)當(dāng)前的活動(dòng)請(qǐng)求計(jì)數(shù)啟動(dòng)請(qǐng)求或?qū)⑵渑湃腙?duì)列
// 啟動(dòng)請(qǐng)求
if ([self isActiveRequestCountBelowMaximumLimit]) {
[self startMergedTask:mergedTask];
}
// 排入隊(duì)列
else {
[self enqueueMergedTask:mergedTask];
}
task = mergedTask.task;
這里要首先進(jìn)行判斷
- (BOOL)isActiveRequestCountBelowMaximumLimit {
return self.activeRequestCount < self.maximumActiveDownloads;
}
self.activeRequestCount
這個(gè)就是活動(dòng)的計(jì)數(shù)器荐类,這個(gè)self.maximumActiveDownloads
值為4怖现,代表最大的下載數(shù)。超過(guò)這個(gè)值就排入隊(duì)列玉罐,小于這個(gè)限制就開(kāi)始任務(wù)屈嗤。
這個(gè)思想很值得我們?nèi)W(xué)習(xí)啊~~~
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
sessionManager.responseSerializer = [AFImageResponseSerializer serializer];
return [self initWithSessionManager:sessionManager
downloadPrioritization:AFImageDownloadPrioritizationFIFO
maximumActiveDownloads:4
imageCache:[[AFAutoPurgingImageCache alloc] init]];
}
后記
本篇主要講述了有關(guān)圖像下載
AFImageDownloader
。