[iOS开发]AFNetworking源码学习(二)

UIKit+AFNetworking

在我们前面讲过的代码以外,还有很大一个部分,那就是UIKit+AFNetworking。小菜鸡前面使用过SDWebImage也看过了一部分的源码,发现他们十分地类似。首先UIImageView+AFNetworking,他这个功能类似于sdwebimage的图片缓存功能,一共使用UIImageView+AFNetworkingAFImageDownloaderAFAutoPurgingImageCache三个类实现这个工,一共是对用户封装的功能,主要异步调用网络加载图片,并且缓存图片不会造成重复加载。AFImageDownloader是负责利用AFNetworking进行下载,并且按照其中的图片格式进行解析,AFAutoPurgingImageCache是为了对图片进行缓存。可以有效预防重复加载,减少网络的请求。看起来就和SDWebImage很像。在这里,我截取了图片,大家可以看一下,是真滴像:

UIKit+AFNetworkingUIKit+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;方法取出缓存图像,并且在NSURLRequestUseProtocolCachePolicyNSURLRequestReturnCacheDataElseLoadNSURLRequestReturnCacheDataDontLoad情况下,取出来图像,如果不为空就调用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对象。这里uploadProgressdownloadProgress都传递为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。如果errornil,在这里接着进行判断,如果需要缓存图像,那么就调用方法进行缓存;接下来遍历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;
        }
    });
}

为了保护线程安全,基本上所有的操作都是同步执行的。
以上就是简单介绍了,对它的实现流程有了这么大致一个了解就好啦。

猜你喜欢

转载自blog.csdn.net/weixin_52192405/article/details/124682107