该文章阅读的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