iOS-網(wǎng)絡(luò)-AFNetworking添加緩存層

為什么要添加一層緩存層?

  1. 系統(tǒng)有默認(rèn)的緩存機(jī)制,用自己的緩存機(jī)制有絕對(duì)把控權(quán)
  2. 緩存的時(shí)效(A到B頁面,B頁面返回,惡意返回反復(fù)操作)
  3. 封裝AFN沒有的功能(如多網(wǎng)絡(luò)任務(wù)異步、斷點(diǎn)下載
  4. 隔離AFN框架,AFN對(duì)項(xiàng)目的影響就比較小(隔離了業(yè)務(wù))

1. 緩存機(jī)制

緩存機(jī)制.png

2. 緩存核心代碼

/**
 核心方法

 @param method 請求方式
 @param urlStr 請求路徑
 @param parameters 參數(shù)
 @param ignoreCache 是否不忽略緩存
 @param cacheDuration 緩存時(shí)效
 @param completionHandler 回調(diào)
 */
- (void)taskWithMethod:(NSString*)method
             urlString:(NSString*)urlStr
            parameters:(NSDictionary *)parameters
           ignoreCache:(BOOL)ignoreCache
         cacheDuration:(NSTimeInterval)cacheDuration
     completionHandler:(SYRequestCompletionHandler)completionHandler{
    
    // 1 url+參數(shù) 生成唯一碼
    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(), ^{
                // 沒有進(jìn)行網(wǎng)絡(luò)請求也回調(diào)異常block
                if (weakSelf.exceptionBlock) {
                    weakSelf.exceptionBlock(nil, localCache);
                }
                // 將緩存數(shù)據(jù)回調(diào)過去
                completionHandler(nil, YES, localCache); //error isCache result
            });
            return;
        }
    }
    
    // 5 處理網(wǎng)絡(luò)返回來的數(shù)據(jù),即緩存處理, 或者直接寫個(gè)方法也可以
    SYRequestCompletionHandler newCompletionBlock = ^( NSError* error,  BOOL isCache, NSDictionary* result){
        //5.1處理緩存  ??參數(shù)ignoreCache(網(wǎng)絡(luò)task發(fā)起前,是否從本來緩存中獲取數(shù)據(jù))  cacheDuration(網(wǎng)絡(luò)task結(jié)束后,是否對(duì)網(wǎng)絡(luò)數(shù)據(jù)緩存)
        result = [NSMutableDictionary dictionaryWithDictionary:result];
        if (cacheDuration > 0) {// 緩存時(shí)效(即緩存時(shí)間)大于0
            if (result) {
                //存入緩存的條件block (比如:如果服務(wù)器數(shù)據(jù)有問題就不存入緩存)
                if (weakSelf.cacheConditionBlock) {
                    if (weakSelf.cacheConditionBlock(result)) { //根據(jù)result判斷是否符合條件
                        [weakSelf.cache saveCacheData:result forKey:fileKeyFromUrl];
                    }
                }else{
                    [weakSelf.cache saveCacheData:result forKey:fileKeyFromUrl];
                }
            }
        }
        
        //5.2 其他情況異?;卣{(diào)
        dispatch_async(dispatch_get_main_queue(), ^{
            if (weakSelf.exceptionBlock) {
                weakSelf.exceptionBlock(error, (NSMutableDictionary*)result);
            }
            completionHandler(error, NO, result);
        });
    };
    
    //3  發(fā)起AF網(wǎng)絡(luò)任務(wù)
    NSURLSessionTask *task = nil;
    if ([method isEqualToString:@"GET"]) {
        task = [self.afHttpManager  GET:urlStr parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            /*
             4 處理數(shù)據(jù) (處理數(shù)據(jù)的時(shí)候,需要處理下載的網(wǎng)絡(luò)數(shù)據(jù)是否要緩存)
             這里可以直接使用 completionHandler,如果這樣,網(wǎng)絡(luò)返回的數(shù)據(jù)沒有做緩存處理機(jī)制
             */
            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];
}

關(guān)于上面的代碼:
第1步:生成唯一的key作為緩存的文件名
第2步:不忽略緩存并且緩存沒失效就使用緩存
第3步:其他情況使用AFN
第4步:AFN請求成功,需要處理數(shù)據(jù)
第5步:處理網(wǎng)絡(luò)返回來的數(shù)據(jù),即是否寫入緩存

3. 多網(wǎng)絡(luò)任務(wù)異步

//tasks數(shù)組里面拿到任務(wù), 使用dispatch_group_t執(zhí)行
- (void)syBatchOfRequestOperations:(NSArray<SYNetRequestInfo *> *)tasks
                     progressBlock:(void (^)(NSUInteger numberOfFinishedTasks, NSUInteger totalNumberOfTasks))progressBlock
                   completionBlock:(netSuccessbatchBlock)completionBlock{
    
    /*
     使用 dispatch_group_t 技術(shù)點(diǎn)
     dispatch_group_enter: 對(duì)group里面的任務(wù)數(shù) +1
     dispatch_group_leave: 任務(wù)完成后,對(duì)group里面的任務(wù)數(shù) -1
     dispatch_group_notify: 當(dāng)group的任務(wù)數(shù)為0了,就會(huì)執(zhí)行notify的block塊操作,即所有的網(wǎng)絡(luò)任務(wù)請求完了。
     */
    __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) {
                // 網(wǎng)絡(luò)任務(wù)啟動(dòng)前dispatch_group_enter
                dispatch_group_enter(group);
                
                SYRequestCompletionHandler newCompletionBlock = ^( NSError* error,  BOOL isCache, NSDictionary* result){
                    //先執(zhí)行進(jìn)度block
                    progressBlock(finishedTasksCount, totalNumberOfTasks);
                    //再執(zhí)行完成block
                    if (obj.completionBlock) {
                        obj.completionBlock(error, isCache, result);
                    }

                    // 網(wǎng)絡(luò)任務(wù)結(jié)束后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];
                }
            }
        }];
        //監(jiān)聽
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            [weakSelf.batchGroups removeObject:group];
            if (completionBlock) {
                completionBlock(tasks);
            }
        });
    });
}

關(guān)于上面的代碼:

  1. (NSArray<SYNetRequestInfo *> *)tasks 數(shù)組里面存放的是一個(gè)任務(wù)對(duì)象
  2. 關(guān)于dispatch_group_t:
    dispatch_group_enter:對(duì)group里面的任務(wù)數(shù) +1。
    dispatch_group_leave:任務(wù)完成后,對(duì)group里面的任務(wù)數(shù) -1。
    dispatch_group_notify:當(dāng)group的任務(wù)數(shù)為0了,就會(huì)執(zhí)行notify的block塊操作,即所有的網(wǎng)絡(luò)任務(wù)請求完了。
  3. 注意newCompletionBlock里面做了兩件事:
    ① 回調(diào)一下任務(wù)進(jìn)度block
    ② 回調(diào)單個(gè)任務(wù)的完成block
  4. 在dispatch_group_notify里面監(jiān)聽全部任務(wù)完成的block,然后回調(diào)

Demo地址:AFN添加緩存層

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容