为什么要添加一层缓存层?
- 系统有默认的缓存机制,用自己的缓存机制有绝对把控权
- 缓存的时效(A到B页面,B页面返回,恶意返回反复操作)
- 封装AFN没有的功能(如多网络任务异步、断点下载)
- 隔离AFN框架,AFN对项目的影响就比较小(隔离了业务)
1. 缓存机制
缓存机制.png
2. 缓存核心代码
/**
核心方法
@param method 请求方式
@param urlStr 请求路径
@param parameters 参数
@param ignoreCache 是否不忽略缓存
@param cacheDuration 缓存时效
@param completionHandler 回调
*/
- (void)taskWithMethod:(NSString*)method
urlString:(NSString*)urlStr
parameters:(NSDictionary *)parameters
ignoreCache:(BOOL)ignoreCache
cacheDuration:(NSTimeInterval)cacheDuration
completionHandler:(SYRequestCompletionHandler)completionHandler{
// 1 url+参数 生成唯一码
NSString *fileKeyFromUrl = SYConvertMD5FromParameter(urlStr, method, parameters);
__weak typeof(self) weakSelf = self;
// 2 缓存+没失效 判断是否有有效缓存
if (!ignoreCache && [self.cache checkIfShouldUseCacheWithCacheDuration:cacheDuration cacheKey:fileKeyFromUrl]) {
NSMutableDictionary *localCache = [NSMutableDictionary dictionary];
NSDictionary *cacheDict = [self.cache searchCacheWithUrl:fileKeyFromUrl];
[localCache setDictionary:cacheDict];
if (cacheDict) {
dispatch_async(dispatch_get_main_queue(), ^{
// 没有进行网络请求也回调异常block
if (weakSelf.exceptionBlock) {
weakSelf.exceptionBlock(nil, localCache);
}
// 将缓存数据回调过去
completionHandler(nil, YES, localCache); //error isCache result
});
return;
}
}
// 5 处理网络返回来的数据,即缓存处理, 或者直接写个方法也可以
SYRequestCompletionHandler newCompletionBlock = ^( NSError* error, BOOL isCache, NSDictionary* result){
//5.1处理缓存 ⚠️参数ignoreCache(网络task发起前,是否从本来缓存中获取数据) cacheDuration(网络task结束后,是否对网络数据缓存)
result = [NSMutableDictionary dictionaryWithDictionary:result];
if (cacheDuration > 0) {// 缓存时效(即缓存时间)大于0
if (result) {
//存入缓存的条件block (比如:如果服务器数据有问题就不存入缓存)
if (weakSelf.cacheConditionBlock) {
if (weakSelf.cacheConditionBlock(result)) { //根据result判断是否符合条件
[weakSelf.cache saveCacheData:result forKey:fileKeyFromUrl];
}
}else{
[weakSelf.cache saveCacheData:result forKey:fileKeyFromUrl];
}
}
}
//5.2 其他情况异常回调
dispatch_async(dispatch_get_main_queue(), ^{
if (weakSelf.exceptionBlock) {
weakSelf.exceptionBlock(error, (NSMutableDictionary*)result);
}
completionHandler(error, NO, result);
});
};
//3 发起AF网络任务
NSURLSessionTask *task = nil;
if ([method isEqualToString:@"GET"]) {
task = [self.afHttpManager GET:urlStr parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
/*
4 处理数据 (处理数据的时候,需要处理下载的网络数据是否要缓存)
这里可以直接使用 completionHandler,如果这样,网络返回的数据没有做缓存处理机制
*/
newCompletionBlock(nil,NO, responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
newCompletionBlock(error,NO, nil);;
}];
}else{
task = [self.afHttpManager POST:urlStr parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
newCompletionBlock(nil,NO, responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
newCompletionBlock(error,NO, nil);
}];
}
[task resume];
}
关于上面的代码:
第1步:生成唯一的key作为缓存的文件名
第2步:不忽略缓存并且缓存没失效就使用缓存
第3步:其他情况使用AFN
第4步:AFN请求成功,需要处理数据
第5步:处理网络返回来的数据,即是否写入缓存
3. 多网络任务异步
//tasks数组里面拿到任务, 使用dispatch_group_t执行
- (void)syBatchOfRequestOperations:(NSArray *)tasks
progressBlock:(void (^)(NSUInteger numberOfFinishedTasks, NSUInteger totalNumberOfTasks))progressBlock
completionBlock:(netSuccessbatchBlock)completionBlock{
/*
使用 dispatch_group_t 技术点
dispatch_group_enter: 对group里面的任务数 +1
dispatch_group_leave: 任务完成后,对group里面的任务数 -1
dispatch_group_notify: 当group的任务数为0了,就会执行notify的block块操作,即所有的网络任务请求完了。
*/
__weak typeof(self) weakSelf = self;
dispatch_async(_SYNetQueue, ^{
__block dispatch_group_t group = dispatch_group_create();
[weakSelf.batchGroups addObject:group];
__block NSInteger finishedTasksCount = 0;
__block NSInteger totalNumberOfTasks = tasks.count;
[tasks enumerateObjectsUsingBlock:^(SYNetRequestInfo * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj) {
// 网络任务启动前dispatch_group_enter
dispatch_group_enter(group);
SYRequestCompletionHandler newCompletionBlock = ^( NSError* error, BOOL isCache, NSDictionary* result){
//先执行进度block
progressBlock(finishedTasksCount, totalNumberOfTasks);
//再执行完成block
if (obj.completionBlock) {
obj.completionBlock(error, isCache, result);
}
// 网络任务结束后dispatch_group_leave
dispatch_group_leave(group);
};
if ([obj.method isEqual:@"POST"]) {
[[SYNetMananger sharedInstance] syPostWithURLString:obj.urlStr parameters:obj.parameters ignoreCache:obj.ignoreCache cacheDuration:obj.cacheDuration completionHandler:newCompletionBlock];
}else{
[[SYNetMananger sharedInstance] syGetWithURLString:obj.urlStr parameters:obj.parameters ignoreCache:obj.ignoreCache cacheDuration:obj.cacheDuration completionHandler:newCompletionBlock];
}
}
}];
//监听
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[weakSelf.batchGroups removeObject:group];
if (completionBlock) {
completionBlock(tasks);
}
});
});
}
关于上面的代码:
- (NSArray
*)tasks 数组里面存放的是一个任务对象 - 关于dispatch_group_t:
dispatch_group_enter:对group里面的任务数 +1。
dispatch_group_leave:任务完成后,对group里面的任务数 -1。
dispatch_group_notify:当group的任务数为0了,就会执行notify的block块操作,即所有的网络任务请求完了。 - 注意newCompletionBlock里面做了两件事:
① 回调一下任务进度block
② 回调单个任务的完成block - 在dispatch_group_notify里面监听全部任务完成的block,然后回调
Demo地址:AFN添加缓存层