UIKit+AFNetworking
在我们前面讲过的代码以外,还有很大一个部分,那就是UIKit+AFNetworking
。小菜鸡前面使用过SDWebImage
也看过了一部分的源码,发现他们十分地类似。首先UIImageView+AFNetworking
,他这个功能类似于sdwebimage
的图片缓存功能,一共使用UIImageView+AFNetworking
、AFImageDownloader
、AFAutoPurgingImageCache
三个类实现这个工,一共是对用户封装的功能,主要异步调用网络加载图片,并且缓存图片不会造成重复加载。AFImageDownloader
是负责利用AFNetworking
进行下载,并且按照其中的图片格式进行解析,AFAutoPurgingImageCache
是为了对图片进行缓存。可以有效预防重复加载,减少网络的请求。看起来就和SDWebImage
很像。在这里,我截取了图片,大家可以看一下,是真滴像:
UIKit+AFNetworking
SDWebImage(部分)
好像哦,哈哈哈。下面,我将以UIImageView+AFNetworking
为例,探索其中的奥秘。我们先看一下基本使用:
UIImageView+AFNetworking
[imageView setImageWithURL:url placeholderImage:nil];
哇,似乎和SDWebImage
的使用方法很像诶!我们看看设置方法:
- (void)setImageWithURL:(NSURL *)url {
[self setImageWithURL:url placeholderImage:nil];
}
- (void)setImageWithURL:(NSURL *)url
placeholderImage:(UIImage *)placeholderImage
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
[self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}
发现,最终调用了如下方法,流程用注释大概解释了。
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(UIImage *)placeholderImage
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse * _Nullable response, NSError *error))failure
{
//容错处理
//这里如果request的URL不存在的话,那就无法请求了,
//这里就将当前UIImageView的image设置为palceHolder图像,并取消该图像下载任务
if ([urlRequest URL] == nil) {
self.image = placeholderImage;
if (failure) {
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil];
failure(urlRequest, nil, error);
}
return;
}
//下面就看一下根据URL判断任务是否存在,如果存在就return,接着就是取消图像下载任务
if ([self isActiveTaskURLEqualToURLRequest:urlRequest]){
return;
}
[self cancelImageDownloadTask];
//获取缓存图像并做决策
//首先是获取下载器,然后获取下载器的缓存,最后根据请求request获取UIImage
AFImageDownloader *downloader = [[self class] sharedImageDownloader];
id <AFImageRequestCache> imageCache = downloader.imageCache;
//Use the image from the image cache if it exists
UIImage *cachedImage = [imageCache imageforRequest:urlRequest withAdditionalIdentifier:nil];
//如果缓存图像存在,如果success的block存在就回调出去,否则就赋值给image。
//最后还是调用clearActiveDownloadInformation,清除下载信息
if (cachedImage) {
if (success) {
success(urlRequest, nil, cachedImage);
} else {
self.image = cachedImage;
}
[self clearActiveDownloadInformation];
} else {
//缓存图像不存在
if (placeholderImage) {
//暂时将图像设置为占位符
self.image = placeholderImage;
}
__weak __typeof(self)weakSelf = self;
NSUUID *downloadID = [NSUUID UUID];
AFImageDownloadReceipt *receipt;
//用下载器进行下载,不管成功还是失败都进行相应的回调,并清除下载信息clearActiveDownloadInformation。
//并在成功的时候设置图像替换掉下载图strongSelf.image = responseObject
receipt = [downloader
downloadImageForURLRequest:urlRequest
withReceiptID:downloadID
success:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, UIImage * _Nonnull responseObject) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
if (success) {
success(request, response, responseObject);
} else if(responseObject) {
strongSelf.image = responseObject;
}
[strongSelf clearActiveDownloadInformation];
}
}
failure:^(NSURLRequest * _Nonnull request, NSHTTPURLResponse * _Nullable response, NSError * _Nonnull error) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
if ([strongSelf.af_activeImageDownloadReceipt.receiptID isEqual:downloadID]) {
if (failure) {
failure(request, response, error);
}
[strongSelf clearActiveDownloadInformation];
}
}];
//根据下载器返回的凭据,更新内存中的有效凭据self.af_activeImageDownloadReceipt
self.af_activeImageDownloadReceipt = receipt;
}
}
下面看一下取消任务的方法:
- (void)cancelImageDownloadTask {
if (self.af_activeImageDownloadReceipt != nil) {
[[self.class sharedImageDownloader] cancelTaskForImageDownloadReceipt:self.af_activeImageDownloadReceipt];
[self clearActiveDownloadInformation];
}
}
- 首先就是判断下载任务的凭据是否存在,如果不存在不用管,说明没有这个任务,这里只处理有这个任务的情况。
- 调用下载器的
cancelTaskForImageDownloadReceipt:
方法,带入凭据参数,取消下载任务。 - 初始化凭据参数,置为
nil
,实现过程如下
- (void)clearActiveDownloadInformation {
self.af_activeImageDownloadReceipt = nil;
}
我们不难看出,其中使用了AFImageDownloader
来进行下载操作,下面就看一下具体是怎么实现的吧
AFImageDownloader
我们去看使用的地方:
AFImageDownloader *downloader = [[self class] sharedImageDownloader];
逐层查看:
+ (AFImageDownloader *)sharedImageDownloader {
return objc_getAssociatedObject(self, @selector(sharedImageDownloader)) ?: [AFImageDownloader defaultInstance];
}
就来到了AFImageDownloader
里面,首先调用的是它的类方法:
+ (instancetype)defaultInstance {
static AFImageDownloader *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
可以看到,最终的初始化过程如下:
- (instancetype)init {
NSURLSessionConfiguration *defaultConfiguration = [self.class defaultURLSessionConfiguration];
return [self initWithSessionConfiguration:defaultConfiguration];
}
- (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;
//定义下载任务的顺序,默认FIFO,先进先出-队列模式,还有后进先出-栈模式
self.downloadPrioritizaton = downloadPrioritization;
//最大的下载数
self.maximumActiveDownloads = maximumActiveDownloads;
//自定义的cache
self.imageCache = imageCache;
//队列中的任务,待执行的
self.queuedMergedTasks = [[NSMutableArray alloc] init];
//合并的任务,所有任务的字典
self.mergedTasks = [[NSMutableDictionary alloc] init];
//活跃的request数
self.activeRequestCount = 0;
//用UUID来拼接名字
NSString *name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.synchronizationqueue-%@", [[NSUUID UUID] UUIDString]];
//创建一个串行的queue
self.synchronizationQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);
name = [NSString stringWithFormat:@"com.alamofire.imagedownloader.responsequeue-%@", [[NSUUID UUID] UUIDString]];
//创建井行queue
self.responseQueue = dispatch_queue_create([name cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
看下AFImageDownloadPrioritization
,这个枚举值代表着,一堆图片下载,执行任务的顺序:
typedef NS_ENUM(NSInteger, AFImageDownloadPrioritization) {
//先进先出
AFImageDownloadPrioritizationFIFO,
//后进先出
AFImageDownloadPrioritizationLIFO
};
下面看一下默认网址会话配置(defaultURLSessionConfiguration)是什么样的:
+ (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;
}
设置缓存对象也可以展开:
+ (NSURLCache *)defaultURLCache {
// It's been discovered that a crash will occur on certain versions
// of iOS if you customize the cache.
//
// More info can be found here: https://devforums.apple.com/message/1102182#1102182
//
// When iOS 7 support is dropped, this should be modified to use
// NSProcessInfo methods instead.
if ([[[UIDevice currentDevice] systemVersion] compare:@"8.2" options:NSNumericSearch] == NSOrderedAscending) {
return [NSURLCache sharedURLCache];
}
//设置一个系统缓存,内存缓存为20Mb,磁盘缓存为150Mb
//这是一个系统级别维护的缓存
return [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024
diskCapacity:150 * 1024 * 1024
diskPath:@"com.alamofire.imagedownloader"];
}
下面再看一下下载的核心方法:
- (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 {
//同步串行去下载,生成一个task,不必担心线程问题
__block NSURLSessionDataTask *task = nil;
dispatch_sync(self.synchronizationQueue, ^{
//url字符串
NSString *URLIdentifier = request.URL.absoluteString;
if (URLIdentifier == nil) {
if (failure) {
//错误返回,没url
NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadURL userInfo:nil];
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
//Task字典中取值,如果有就把以前的Task赋值回去,避免重复创建Task
AFImageDownloaderMergedTask *existingMergedTask = self.mergedTasks[URLIdentifier];
if (existingMergedTask != nil) {
//里面包含成功和失败B1ock和uUid
AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID success:success failure:failure];
//添加handler
[existingMergedTask addResponseHandler:handler];
//给task赋值
task = existingMergedTask.task;
return;
}
// 2) Attempt to load the image from the image cache if the cache policy allows it
//3种情况都会去加载缓存缓存策略有值
switch (request.cachePolicy) {
//这三种策略都会加载图片
case NSURLRequestUseProtocolCachePolicy://NSURLRequest默认的cache policy,使用Protoco1协议定义
case NSURLRequestReturnCacheDataElseLoad://只有在cache中不存在data时才从原始地址下载
case NSURLRequestReturnCacheDataDontLoad: {
//只使用cache数据,如果不存在cache,请求失败;用于没有建立网络连接离线模式
//从cache中根据request拿数据
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
//走到这说明即没有请求中的request,也没有cache,开始请求
NSUUID *mergedTaskIdentifier = [NSUUID UUID];
NSURLSessionDataTask *createdTask;
__weak __typeof__(self) weakSelf = self;
//用sessionManager的去请求,注意,只是创建task,还是挂起状态
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
//创建handler
AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID
success:success
failure:failure];
//创建task
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
//如果小于,则开始任务下载resume
if ([self isActiveRequestCountBelowMaximumLimit]) {
[self startMergedTask:mergedTask];
} else {
[self enqueueMergedTask:mergedTask];
}
//拿到最终生成的task
task = mergedTask.task;
});
if (task) {
//创建一个AFImageDownloadReceipt并返回,里面就多一个receiptID
return [[AFImageDownloadReceipt alloc] initWithReceiptID:receiptID task:task];
} else {
return nil;
}
}
可以看到下载的时候是在self.synchronizationQueue
里做的,看一下这个串行队列的初始化:
self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
downloadImageForURLRequest:
方法主要做了这么几件事:
1)向预先存在请求中添加成功失败回调块
如果成功和失败块已经存在,则将其添加到预先存在的请求中,主要对应下面这段代码:
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
这个类:
@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
//初始化对象
- (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];
}
@end
这里有一个数组responseHandlers
里面存放的类型就是AFImageDownloaderResponseHandler
:
@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
这个AFImageDownloaderResponseHandler
对象将UUID、成功回调和失败回调作为属性包括了进去,方便我们使用。
下面还是回来到下载图像的源代码中,这个类里面有一个属性:
@property (nonatomic, strong) NSMutableDictionary *mergedTasks;
这是一个字典,URLIdentifier
作为key
,取出来的就是AFImageDownloaderMergedTask
对象,如果请求任务已经存在,那么就实例化AFImageDownloaderResponseHandler
对象并添加到数组中。并且进行赋值task = existingMergedTask.task
,取出来存在的task
传给自定义的task
对象。最后return
返回。
2)从缓存中添加图像
switch (request.cachePolicy) {
//这三种策略都会加载图片
case NSURLRequestUseProtocolCachePolicy://NSURLRequest默认的cache policy,使用Protoco1协议定义
case NSURLRequestReturnCacheDataElseLoad://只有在cache中不存在data时才从原始地址下载
case NSURLRequestReturnCacheDataDontLoad: {
//只使用cache数据,如果不存在cache,请求失败;用于没有建立网络连接离线模式
//从cache中根据request拿数据
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;
}
这段代码还是比较好理解的,利用- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(nullable NSString *)identifier;
方法取出缓存图像,并且在NSURLRequestUseProtocolCachePolicy
、 NSURLRequestReturnCacheDataElseLoad
和NSURLRequestReturnCacheDataDontLoad
情况下,取出来图像,如果不为空就调用success
的回调。下面是获取图片的方法代码:
- (nullable UIImage *)imageforRequest:(NSURLRequest *)request withAdditionalIdentifier:(NSString *)identifier {
return [self imageWithIdentifier:[self imageCacheKeyFromURLRequest:request withAdditionalIdentifier:identifier]];
}
- (nullable UIImage *)imageWithIdentifier:(NSString *)identifier {
__block UIImage *image = nil;
dispatch_sync(self.synchronizationQueue, ^{
AFCachedImage *cachedImage = self.cachedImages[identifier];
image = [cachedImage accessImage];
});
return image;
}
3)创建请求,设置权限验证和响应序列化
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];
});
}];
调用AFHTTPSessionManager
类中的方法返回NSURLSessionDataTask *createdTask
对象。这里uploadProgress
和downloadProgress
都传递为nil
,并且在回调完成的方法中,生成异步并行队列进行处理。这里首先取出对象AFImageDownloaderMergedTask *mergedTask
,然后根据[mergedTask.identifier isEqual:mergedTaskIdentifier]
进行比较,满足了以后调用下面方法返回mergedTask
对象,并将其对应的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;
}
接着就是根据回调error
参数,进行判断,如果error
不为空,也就是存在错误,那么就遍历responseHandlers
数组,找到其对应的failureBlock
属性,并在主线程回调block
。如果error
为nil
,在这里接着进行判断,如果需要缓存图像,那么就调用方法进行缓存;接下来遍历responseHandlers
数组,找到其对应的successBlock
属性,并在主线程回调block
。接下来看一下最后两个方法的实现:
// 减小任务计数
- (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;
}
4)请求完成时存储响应处理程序以备使用
// AFImageDownloaderResponseHandler实例化
AFImageDownloaderResponseHandler *handler = [[AFImageDownloaderResponseHandler alloc] initWithUUID:receiptID
success:success
failure:failure];
// AFImageDownloaderMergedTask实例化
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;
这里要首先进行判断:
- (BOOL)isActiveRequestCountBelowMaximumLimit {
return self.activeRequestCount < self.maximumActiveDownloads;
}
self.activeRequestCount
这个就是活动的计数器,这个self.maximumActiveDownloads
值为4,代表最大的下载数。超过这个值就排入队列,小于这个限制就开始任务。这个值是在前面初始化时确定的:
- (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]];
}
最后返回一个AFImageDownloadReceipt
,里面有两个属性,看一下创建过程:
- (instancetype)initWithReceiptID:(NSUUID *)receiptID task:(NSURLSessionDataTask *)task {
if (self = [self init]) {
self.receiptID = receiptID;
self.task = task;
}
return self;
}
AFAutoPurgingImageCache
- (instancetype)init {
return [self initWithMemoryCapacity:100 * 1024 * 1024 preferredMemoryCapacity:60 * 1024 * 1024];
}
- (instancetype)initWithMemoryCapacity:(UInt64)memoryCapacity preferredMemoryCapacity:(UInt64)preferredMemoryCapacity {
if (self = [super init]) {
self.memoryCapacity = memoryCapacity;
self.preferredMemoryUsageAfterPurge = preferredMemoryCapacity;
self.cachedImages = [[NSMutableDictionary alloc] init];
NSString *queueName = [NSString stringWithFormat:@"com.alamofire.autopurgingimagecache-%@", [[NSUUID UUID] UUIDString]];
self.synchronizationQueue = dispatch_queue_create([queueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_CONCURRENT);
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(removeAllImages)
name:UIApplicationDidReceiveMemoryWarningNotification
object:nil];
}
return self;
}
首先初始化了最大内存数量,以及最佳缓存数量。然后初始化一个并行队列,还有监听清内存警告。下面介绍一个核心方法。
//添加image到cache里
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier {
//用dispatch_barrier_async来同步队列
dispatch_barrier_async(self.synchronizationQueue, ^{
AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier];
//去之前的cache字典里取
AFCachedImage *previousCachedImage = self.cachedImages[identifier];
//如果被缓存过
if (previousCachedImage != nil) {
//当前已经使用的内存大小减去图片的大小
self.currentMemoryUsage -= previousCachedImage.totalBytes;
}
//把新cache的image加上去
self.cachedImages[identifier] = cacheImage;
//加上内存大小
self.currentMemoryUsage += cacheImage.totalBytes;
});
//做缓存溢出的清除,清除的是早期的缓存
dispatch_barrier_async(self.synchronizationQueue, ^{
//如果使用的内存大于我们设置的内存容量
if (self.currentMemoryUsage > self.memoryCapacity) {
//拿到使用内存-被清空后首选内存=需要被清除的内存
UInt64 bytesToPurge = self.currentMemoryUsage - self.preferredMemoryUsageAfterPurge;
//拿到所有缓存的数据
NSMutableArray <AFCachedImage*> *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues];
//根据lastAccessDate排序升序,越晚的越后面
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate"
ascending:YES];
[sortedImages sortUsingDescriptors:@[sortDescriptor]];
UInt64 bytesPurged = 0;
//移除早期的cachebytesToPuzge大小
for (AFCachedImage *cachedImage in sortedImages) {
[self.cachedImages removeObjectForKey:cachedImage.identifier];
bytesPurged += cachedImage.totalBytes;
if (bytesPurged >= bytesToPurge) {
break ;
}
}
//减去被清掉的内存
self.currentMemoryUsage -= bytesPurged;
}
});
}
为了保护线程安全,基本上所有的操作都是同步执行的。
以上就是简单介绍了,对它的实现流程有了这么大致一个了解就好啦。