AFNetworking的缓存机制
AFNetworking实际上使用了两个缓存机制:AFImageCache和NSURLCache.
一.AFImageCache
协议定义了一组API,用于同步添加,删除和从缓存中提取图像。
AFImageRequestCache
协议扩展了AFImageCache
协议,增加了通过request和 additional identifier同步添加,删除和从缓存中提取图像的方法。
AFAutoPurgingImageCache
实现了AFImageRequestCache
协议。 这个类给了我们临时管理图片内存的能力。我们把图片缓存到一个字典cachedImages中。缓存使用的是一个同步的队列 。
处理容量超过最大容量的异常情况。分为下边几个步骤: 1.比较当前使用的容量是否超过最大容量 2.计算将要清除的缓存容量 3.把所有缓存的图片放到一个数组中 4.对这个数组按照最后访问时间进行排序,优先保留最后访问的数据 5.遍历数组,移除图片(当已经移除的数据大于应该移除的数据时停止)
注:每次通过缓存访问图像时,都会更新图像的内部访问日期。
AFImageDownloader
类负责在优先级队列上并行下载图像。 传入的下载将添加到队列的前面或后面,具体取决于下载的优先级。 每个下载的图像都缓存在底层的NSURLCache
以及内存中的图像缓存中。 默认情况下,在图像缓存中具有等效缓存图像的任何下载请求将自动为缓存的图像表示提供服务。
用于下载图像的AFHTTPSessionManager。 默认情况下,它配置了一个AFImageResponseSerializer,以及一个用于所有图像下载的共享NSURLCache。
+ (NSURLCache *)defaultURLCache {
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"];
}
1)将成功和失败块附加到预先存在的请求(如果已存在)
2)如果缓存策略允许,则尝试从缓存加载图片
3)创建请求并设置身份验证,有效性验证和响应序列化,
4)存储响应处理程序以在请求完成时使用
5)根据当前活动请求计数启动请求或将其入队
下面是1)~ 5)的详细介绍
1)创建NSURLSessionDataTask是在dispatch_sync中使用self.synchronizationQueue队列创建的。我们可以通过request.URL.absoluteString拿到URLIdentifier。将成功和失败块附加到预先存在的请求。
(注:如果我调用了多次核心方法 ,但是URLIdentifier都一样,也就是说我请求了同一个图片,为了性能,它们会共用同一资源但处理各自的响应结果事件。
举个例子,比如说我有 图片1, 用它我来做3件事,(doSomething1, doSomething2, doSomething3),我发了3个请求,但是我要的结果应该拿到 图片1,同时能够单独调用doSomething1, doSomething2, doSomething3。
用AFImageDownloaderMergedTask 任务合并对象来处理这个问题。在 AFImageDownloader 中的self.mergedTasks字典中放着所有的合并的任务。key就是URLIdentifier。)
2)我们需要一个 AFImageDownloaderMergedTask ,我们先在self.mergedTasks中取,取出后把事件绑定到AFImageDownloaderMergedTask中,然后再取出它的task。
那么如果在self.mergedTasks中没有这个task,(如果缓存策略支持去缓存当中取)我们首先应该在内存中去看看image是否存在,如果存在,我们只需要调用success就行了。
3)创建task的回调
createdTask = [self.sessionManager
dataTaskWithRequest:request
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
// 由于我们把所有的事件处理都绑定在AFImageDownloaderMergedTask中。所以我们第一步要先获取这个对象
1. 获取AFImageDownloaderMergedTask
// 确保这个mergedTask跟这个createdTask是同一个
2. 如果mergedTask跟这个createdTask是同一个
// 虽然self.mergedTasks中也能取到mergedTask,但我们最终用到的数据还是来自于数组中取出这个mergedTask,取出后要删除掉
3.在mergedTask数组中取出这个mergedTask后删除数组中的这个mergedTask
if (错误) {
4. 遍历mergedTask中的绑定事件中的错误block
}else {
5. 把图片添加进缓存
6. 遍历mergedTask中的绑定事件中的成功block
}
7.当前的请求数减1
8.开启下一个请求
}];
4)为了防止重复的请求专门封装了AFImageDownloaderMergedTask这个类。我们要把这个task包装成AFImageDownloaderMergedTask类型,然后
添加到 AFImageDownloader 的self.mergedTasks字典中。
5)if (当前请求数小于支持的最大请求数) {
发起请求
}else {
按照我们设置的请求优先级对task进行入队 //FIFO或LIFO
}
2⃣️NSURLCache:
仍使用原生缓存机制:NSURLCache。NSURLConnection’s默认的URL缓存机制,用于存储NSURLResponse对象:默认缓存在内存,通过配置可以缓存到磁盘。NSURLCache对每个NSURLRequest对象都会遵守缓存策略(NSURLRequestCachePolicy)。
在NSURLSessionDataDelegate协议中有一个方法
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
{
NSCachedURLResponse *cachedResponse = proposedResponse;
if (self.dataTaskWillCacheResponse) {
cachedResponse = self.dataTaskWillCacheResponse(session, dataTask, proposedResponse);
}
if (completionHandler) {
completionHandler(cachedResponse);
}
}
//AFURLSessionManager的一个属性和一个方法
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
(void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block;
//设置要执行的块以确定数据任务的缓存行为,由`NSURLSessionDataDelegate`方法`URLSession:dataTask:willCacheResponse:completionHandler:`处理。
苹果系统缓存存储策略:
{
NSURLCacheStorageAllowed, //默认,可以存在内存(重启设备清除),可以存储磁盘(代码清除)
NSURLCacheStorageAllowedInMemoryOnly,
NSURLCacheStorageNotAllowed,
} NSURLCacheStoragePolicy;
iOS对NSURLRequest提供了7种请求缓存策略:
NSURLRequestUseProtocolCachePolicy // 默认的缓存策略(取决于协议)具体工作:如果一个NSCachedURLResponse对于请求并不存在,数据将会从源端获取。如果请求拥有一个缓存的响应,那么URL加载系统会检查这个响应来决定,如果它指定内容必须重新生效的话。假如内容必须重新生效,将建立一个连向源端的连接来查看内容是否发生变化。假如内容没有变化,那么响应就从本地缓存返回数据。如果内容变化了,那么数据将从源端获取
NSURLRequestReloadIgnoringLocalCacheData // 忽略本地缓存,重新请求源端数据
NSURLRequestReloadIgnoringLocalAndRemoteCacheData // 本地缓存数据、代理和其他中介都要忽视他们的缓存,直接加载源数据
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData //设置同上
NSURLRequestReturnCacheDataElseLoad//指定已存的缓存数据应该用来响应请求,不管它的生命时长和过期时间。如果在缓存中没有已存数据来响应请求的话,数据从源端加载。
NSURLRequestReturnCacheDataDontLoad// 指定已存的缓存数据用来满足请求,不管生命时长和过期时间。如果在缓存中没有已存数据来响应URL加载请求的话,不去尝试从源段加载数据,此时认为加载请求失败。这个常量指定了一个类似于离线模式的行为
NSURLRequestReloadRevalidatingCacheData // 指定如果已存的缓存数据被提供它的源端确认为有效则允许使用缓存数据响应请求,否则从源端加载数据。
清除所有的URL缓存Response:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
缓存的设置需要根据具体的情况考虑,如果请求某个URL的返回数据:
(1)经常更新:不能用缓存!比如股票、彩票数据
(2)一成不变:果断用缓存
(3)偶尔更新:可以定期更改缓存策略 或者 清除缓存