該文章閱讀的AFNetworking的版本爲3.2.0。數組
該類用於圖片的下載緩存
typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
AFImageDownloadPrioritizationFIFO,
AFImageDownloadPrioritizationLIFO
};
複製代碼
該枚舉定義了圖片下載的優先級:安全
AFImageDownloadPrioritizationFIFO
表示下載圖片時按照先進先出的原則bash
AFImageDownloadPrioritizationLIFO
表示下載圖片時按照後進先出的原則網絡
這個類是一個公共類,它是對一個task
的封裝,並用UUID做爲標識符。session
/**
AFImageDownloader建立的task
*/
@property (nonatomic, strong) NSURLSessionDataTask *task;
/**
標識符
*/
@property (nonatomic, strong) NSUUID *receiptID;
複製代碼
實現很是簡單,只有一個自定義的初始化方法,將task
和標識符進行保存。async
- (instancetype)initWithReceiptID:(NSUUID *)receiptID task:(NSURLSessionDataTask *)task {
if (self = [self init]) {
self.receiptID = receiptID;
self.task = task;
}
return self;
}
複製代碼
這個類是封裝task
的回調block,並用UUID做爲標識符ide
/**
手機的UUID
*/
@property (nonatomic, strong) NSUUID *uuid;
/**
成功回調block
*/
@property (nonatomic, copy) void (^successBlock)(NSURLRequest*, NSHTTPURLResponse*, UIImage*);
/**
失敗回調block
*/
@property (nonatomic, copy) void (^failureBlock)(NSURLRequest*, NSHTTPURLResponse*, NSError*);
複製代碼
它的實現一樣也是很簡單,一個自定義初始化方法,一個打印數據方法post
- (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]];
}
複製代碼
這個類是將一樣的任務進項合併,但保留回調block,一樣是以UUID做爲標識符ui
/**
圖片連接標識符
*/
@property (nonatomic, strong) NSString *URLIdentifier;
/**
手機的UUID
*/
@property (nonatomic, strong) NSUUID *identifier;
/**
下載任務
*/
@property (nonatomic, strong) NSURLSessionDataTask *task;
/**
響應回調數組
*/
@property (nonatomic, strong) NSMutableArray <AFImageDownloaderResponseHandler*> *responseHandlers;
複製代碼
/**
自定義初始化方法
*/
- (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;
}
/**
添加響應回調
*/
- (void)addResponseHandler:(AFImageDownloaderResponseHandler*)handler {
[self.responseHandlers addObject:handler];
}
/**
刪除響應回調
*/
- (void)removeResponseHandler:(AFImageDownloaderResponseHandler*)handler {
[self.responseHandlers removeObject:handler];
}
複製代碼
/**
圖片緩存對象,默認是AFAutoPurgingImageCache對象
*/
@property (nonatomic, strong, nullable) id <AFImageRequestCache> imageCache;
/**
用來下載圖片的網絡會話管理對象,默認是AFImageResponseSerializer對象
*/
@property (nonatomic, strong) AFHTTPSessionManager *sessionManager;
/**
下載任務隊列的優先級,默認是先進先出
*/
@property (nonatomic, assign) AFImageDownloadPrioritization downloadPrioritizaton;
複製代碼
/**
獲取圖片下載器單例對象
*/
+ (instancetype)defaultInstance;
/**
獲取默認的URL緩存對象
*/
+ (NSURLCache *)defaultURLCache;
/**
獲取默認的網絡會話配置對象
*/
+ (NSURLSessionConfiguration *)defaultURLSessionConfiguration;
/**
默認的初始化方法
*/
- (instancetype)init;
/**
指定網絡會話配置對象的初始化方法
*/
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration;
/**
以指定網絡會話管理者、下載優先級、最大下載數量和圖片緩存對象的初始化方法
*/
- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager
downloadPrioritization:(AFImageDownloadPrioritization)downloadPrioritization
maximumActiveDownloads:(NSInteger)maximumActiveDownloads
imageCache:(nullable id <AFImageRequestCache>)imageCache;
/**
以指定請求建立一個圖片下載的task。可是,若是相同的任務已經在隊列中或者正在下載,就不會建立task,而是直接把成功和失敗回調與原task相關聯
*/
- (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;
/**
以指定請求和標識符建立一個圖片下載的task。可是,若是相同的任務已經在隊列中或者正在下載,就不會建立task,而是直接把成功和失敗回調與原task相關聯。
*/
- (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;
/**
取消下載任務,若是任務在隊列中,任務會被取消;若是任務正在執行或者已完成,成功和失敗回調會被刪除。
*/
- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt;
複製代碼
/**
串行隊列
*/
@property (nonatomic, strong) dispatch_queue_t synchronizationQueue;
/**
響應隊列
*/
@property (nonatomic, strong) dispatch_queue_t responseQueue;
/**
最大下載數
*/
@property (nonatomic, assign) NSInteger maximumActiveDownloads;
/**
當前活動請求數
*/
@property (nonatomic, assign) NSInteger activeRequestCount;
/**
隊列中的任務
*/
@property (nonatomic, strong) NSMutableArray *queuedMergedTasks;
/**
保存任務和其標識符
*/
@property (nonatomic, strong) NSMutableDictionary *mergedTasks;
複製代碼
+ (NSURLCache *)defaultURLCache {
// 這個地方做者解釋說,在某些版本中自定義NSURLCache會致使崩潰。從下面的代碼能夠看出,當系統版本比iOS8.2小的時候用系統默認的緩存方法,不然就是內存20M,硬盤150M的自定義緩存容量
if ([[[UIDevice currentDevice] systemVersion] compare:@"8.2" options:NSNumericSearch] == NSOrderedAscending) {
return [NSURLCache sharedURLCache];
}
return [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024
diskCapacity:150 * 1024 * 1024
diskPath:@"com.alamofire.imagedownloader"];
}
+ (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;
}
- (instancetype)init {
// 實例化網絡會話配置類對象並以之爲參數調用下面的方法
NSURLSessionConfiguration *defaultConfiguration = [self.class defaultURLSessionConfiguration];
return [self initWithSessionConfiguration:defaultConfiguration];
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
// 利用參數實例化AFHTTPSessionManager對象,而後以之爲參數調用下面的方法
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;
NSString *name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.synchronizationqueue-%@", [[NSUUID UUID] UUIDString]];
self.synchronizationQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);
name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.responsequeue-%@", [[NSUUID UUID] UUIDString]];
self.responseQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
+ (instancetype)defaultInstance {
// 生成單例對象
static AFImageDownloader *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (nullable AFImageDownloadReceipt *)downloadImageForURLRequest:(NSURLRequest *)request
success:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, UIImage * _Nonnull))success
failure:(void (^)(NSURLRequest * _Nonnull, NSHTTPURLResponse * _Nullable, NSError * _Nonnull))failure {
// 以標識符爲UUID調用下面的方法
return [self downloadImageForURLRequest:request withReceiptID:[NSUUID UUID] success:success failure:failure];
}
- (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 {
// 生成臨時變量
__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];
dispatch_async(dispatch_get_main_queue(), ^{
failure(request, nil, error);
});
}
return;
}
// 1) 若是已經發送過這個請求,就直接把回調放到數組中等待回調,task直接賦值,再也不生成,而後返回
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) 若是緩存策略容許就從緩存中加載圖像
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) 建立請求並設置認證,驗證和響應序列化
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;
// 獲取到URLIdentifier對應的任務對象
AFImageDownloaderMergedTask *mergedTask = strongSelf.mergedTasks[URLIdentifier];
// 若是是同一個任務對象
if ([mergedTask.identifier isEqual:mergedTaskIdentifier]) {
// 就從任務隊列中刪除
mergedTask = [strongSelf safelyRemoveMergedTaskWithURLIdentifier:URLIdentifier];
// 若是出錯就找出對應的錯誤回調block進行回調
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];
}
// 找出對應的成功回調block進行回調
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) 保存響應處理回調以在請求完成時使用
// 根據標識符、成功回調block和失敗回調block生成圖片下載響應對象
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) 根據當前的活動請求計數啓動請求或將其排入隊列
// 若是沒達到請求數量上限,就啓動任務,不然就排隊
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;
}
}
- (void)cancelTaskForImageDownloadReceipt:(AFImageDownloadReceipt *)imageDownloadReceipt {
dispatch_sync(self.synchronizationQueue, ^{
// 經過圖片封裝對象獲取到任務的連接
NSString *URLIdentifier = imageDownloadReceipt.task.originalRequest.URL.absoluteString;
// 經過連接獲取到任務
AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier];
// 經過標識符獲取到響應對象在數組中的索引
NSUInteger index = [mergedTask.responseHandlers indexOfObjectPassingTest:^BOOL(AFImageDownloaderResponseHandler * _Nonnull handler, __unused NSUInteger idx, __unused BOOL * _Nonnull stop) {
return handler.uuid == imageDownloadReceipt.receiptID;
}];
// 若是找到了圖片封裝對象對應的響應對象
if (index != NSNotFound) {
// 經過索引獲取到響應對象
AFImageDownloaderResponseHandler *handler = mergedTask.responseHandlers[index];
// 從數組中移除掉響應對象
[mergedTask removeResponseHandler:handler];
// 生成錯誤對象並回調錯誤block
NSString *failureReason = [NSString stringWithFormat:@"ImageDownloader cancelled URL request: %@",imageDownloadReceipt.task.originalRequest.URL.absoluteString];
NSDictionary *userInfo = @{NSLocalizedFailureReasonErrorKey:failureReason};
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCancelled userInfo:userInfo];
if (handler.failureBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
handler.failureBlock(imageDownloadReceipt.task.originalRequest, nil, error);
});
}
}
// 若是回調對象爲空,而且任務已經暫停。就取消任務,並移除掉對應的任務合併對象
if (mergedTask.responseHandlers.count == 0 && mergedTask.task.state == NSURLSessionTaskStateSuspended) {
[mergedTask.task cancel];
[self removeMergedTaskWithURLIdentifier:URLIdentifier];
}
});
}
複製代碼
/**
經過標識符安全移除合併任務對象
*/
- (AFImageDownloaderMergedTask*)safelyRemoveMergedTaskWithURLIdentifier:(NSString *)URLIdentifier {
// 串行隊列同步執行移除合併任務對象
__block AFImageDownloaderMergedTask *mergedTask = nil;
dispatch_sync(self.synchronizationQueue, ^{
mergedTask = [self removeMergedTaskWithURLIdentifier:URLIdentifier];
});
return mergedTask;
}
/**
經過標識符移除合併任務對象,這個方法只能在self.synchronizationQueue隊列中安全調用
*/
- (AFImageDownloaderMergedTask *)removeMergedTaskWithURLIdentifier:(NSString *)URLIdentifier {
// 經過標識符從可變字典中獲取合併任務對象
AFImageDownloaderMergedTask *mergedTask = self.mergedTasks[URLIdentifier];
// 經過標識符從可變字典中移除對應的合併任務對象
[self.mergedTasks removeObjectForKey:URLIdentifier];
return mergedTask;
}
/**
安全的減小當前任務活動數量
*/
- (void)safelyDecrementActiveTaskCount {
// 串行隊列同步執行
dispatch_sync(self.synchronizationQueue, ^{
// 減小當前正在活動的請求數量
if (self.activeRequestCount > 0) {
self.activeRequestCount -= 1;
}
});
}
/**
若是必要的話安全的開啓下一個任務
*/
- (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;
}
/**
將合併任務對象添加到排隊中
*/
- (void)enqueueMergedTask:(AFImageDownloaderMergedTask *)mergedTask {
switch (self.downloadPrioritizaton) {
case AFImageDownloadPrioritizationFIFO:
// 若是優先級是先進先出,就添加到最後一個
[self.queuedMergedTasks addObject:mergedTask];
break;
case AFImageDownloadPrioritizationLIFO:
// 若是優先級是後進先出,就插入到第一個
[self.queuedMergedTasks insertObject:mergedTask atIndex:0];
break;
}
}
/**
獲取到排隊中的第一個合併任務對象
*/
- (AFImageDownloaderMergedTask *)dequeueMergedTask {
AFImageDownloaderMergedTask *mergedTask = nil;
mergedTask = [self.queuedMergedTasks firstObject];
[self.queuedMergedTasks removeObject:mergedTask];
return mergedTask;
}
/**
判斷當前活動請求數量是否達到限制數量
*/
- (BOOL)isActiveRequestCountBelowMaximumLimit {
return self.activeRequestCount < self.maximumActiveDownloads;
}
複製代碼
源碼閱讀系列:AFNetworking
源碼閱讀:AFNetworking(二)——AFURLRequestSerialization
源碼閱讀:AFNetworking(三)——AFURLResponseSerialization
源碼閱讀:AFNetworking(四)——AFSecurityPolicy
源碼閱讀:AFNetworking(五)——AFNetworkReachabilityManager
源碼閱讀:AFNetworking(六)——AFURLSessionManager
源碼閱讀:AFNetworking(七)——AFHTTPSessionManager
源碼閱讀:AFNetworking(八)——AFAutoPurgingImageCache
源碼閱讀:AFNetworking(九)——AFImageDownloader
源碼閱讀:AFNetworking(十)——AFNetworkActivityIndicatorManager
源碼閱讀:AFNetworking(十一)——UIActivityIndicatorView+AFNetworking
源碼閱讀:AFNetworking(十二)——UIButton+AFNetworking
源碼閱讀:AFNetworking(十三)——UIImageView+AFNetworking
源碼閱讀:AFNetworking(十四)——UIProgressView+AFNetworking