iOS缓存配置、AFN缓存配置

作者: 4335151e8554 | 来源:发表于2018-12-04 11:03 被阅读778次

    看了很多文章,大部分都是说缓存怎么读取,而缓存的写入都说是系统自动写入,说的不清楚。下面我就自己看过的文章和自己的实践理解,写一篇关于NSURLRequest、NSURLSession和AFNetworking缓存的介绍。

    学习iOS的缓存主要有两个方面,数据写入缓存读取,下面先讲缓存读取

    缓存读取

    只需要设置request请求的cachePolicy属性,就可以控制缓存的读取,很简单。

    • 常用缓存策略
    //使用协议缓存策略,也就是按照响应头的HTTP缓存字段来使用缓存
    NSURLRequestUseProtocolCachePolicy
    //忽略缓存
    NSURLRequestReloadIgnoringLocalCacheData
    //有缓存则读缓存(就算缓存过期也读取),没有则网络加载
    NSURLRequestReturnCacheDataElseLoad
    //有缓存则读缓存(就算缓存过期也读取),没有则报错
    NSURLRequestReturnCacheDataDontLoad
    
    • 下面有三种方法配置request的缓存策略,而且生效优先级依次降低

    1、设置单个请求的缓存策略,直接设置单个请求的缓存策略,其生效的优先级是最高的,不会受到全局缓存配置的影响。

    NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:url];
    request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;//本次请求忽略本地缓存 重新请求数据
    

    2、设置NSURLSession缓存策略,使用该session发出的所有请求都会服从该session的缓存策略,其生效优先级低于上面的单个请求配置

    NSURLSessionConfiguration *confi = [NSURLSessionConfiguration defaultSessionConfiguration];
    confi.requestCachePolicy = NSURLRequestReturnCacheDataElseLoad;
    NSURLSession *session = [NSURLSession sessionWithConfiguration:confi delegate:self 
                                                     delegateQueue:[NSOperationQueue mainQueue]];
    

    3、设置AFNetworking缓存策略,生效优先级低于上面的NSURLSession缓存配置

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
    

    注意点:如果允许使用缓存,在没有网络的情况下,倘若本地有缓存则会走成功的回调返回缓存数据,不会走请求失败的回调

    缓存写入

    缓存的写入所需的条件比较多

    • 系统默认使用NSURLCache做缓存容器,首先我们可以按需设置NSURLCache的大小,我这里设置了4兆的内存20兆的磁盘空间做缓存。如果不需要缓存那么直接设置空间为0.
    NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 
                                                diskCapacity:20 * 1024 * 1024 diskPath:nil];
    [NSURLCache setSharedURLCache:URLCache];    //设置共享缓存容器
    
    • 缓存在写入前会调用NSURLSession的willCacheResponse这个代理方法,并且只有将response传给completionHandler()之后系统才会缓存该响应,如果传入nil则表示不缓存该响应。我们可以在这个方法里修改响应信息,然后保存修改之后的响应信息。(不只是GET请求,POST请求也能触发代理方法,保存响应)
    • 响应信息会被缓存下来的先决条件:首先要满足willCacheResponse代理方法的触发条件(如果这个代理方法都不能触发,那么缓存的写入也无从谈起)。然后将需要缓存的响应传给代理方法的completionHandler()进行保存,如果传入nil则表示不缓存。
      注意:如果设置共享缓存空间为0,那么也不会缓存响应信息

    只有满足以下所有条件时才会触发willCacheResponse代理方法:
    1、请求是针对HTTP或HTTPS URL(或你自己的支持缓存的自定义网络协议)。
    2、请求成功(状态码在200-299范围内)。
    3、返回的响应数据来自服务器,而不是来自本地缓存。(废话,本地缓存不会再缓存一遍)
    下面两个条件是我从其他文章看到的,经过测试发现并不需要
    //4、session会话配置的缓存策略允许缓存。(这个条件好像有问题,会话配置只决定是否使用缓存,而不决定缓存的写入)
    //5、NSURLRequest对象的缓存策略(如果适用)允许缓存。(这也是控制响应的写入)
    6、服务器响应中的缓存相关头(如果存在)允许缓存。也就是响应头中的HTTP缓存字段(这里一下解释不清,请自行查看http的缓存相关字段)
    7、响应大小足够小,可以合理地放入缓存中。 (例如,如果您提供磁盘缓存,则响应不得超过磁盘缓存大小的5%。)
    注:如果代理实现此方法willCacheResponse,则该代理方法中必须调用completionHandler完成处理程序,否则应用程序会泄漏内存。

    • 对willCacheResponse这个代理方法的一些使用
    -(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask 
        willCacheResponse:(NSCachedURLResponse *)proposedResponse 
        completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
    
        //这里可以修改HTTP的缓存字段
        NSURLResponse *response = proposedResponse.response;
        NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse*)response;
        NSDictionary *headers = HTTPResponse.allHeaderFields;
        NSCachedURLResponse *cachedResponse;
        //修改Cache-Control字段的值
        NSMutableDictionary *modifiedHeaders = headers.mutableCopy;
        [modifiedHeaders setObject:@"max-age=10" forKey:@"Cache-Control"];
        NSHTTPURLResponse * modifiedResponse;
        modifiedResponse = [[NSHTTPURLResponse alloc] initWithURL:HTTPResponse.URL 
                                statusCode:HTTPResponse.statusCode HTTPVersion:@"HTTP/1.1" 
                                headerFields:modifiedHeaders];
        cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:modifiedResponse 
                                data:proposedResponse.data
                                userInfo:proposedResponse.userInfo 
                                storagePolicy:proposedResponse.storagePolicy];
    
        //将修改后的响应信息交给该block执行,该block就会保存该响应,当然了也可以不修改响应
        completionHandler(cachedResponse);   //返回nil则表示不缓存响应
    }
    

    AFN缓存

    AFN默认已经实现了willCacheResponse代理方法,只要满足这个代理方法的触发条件,那么该响应就会被缓存到本地。
    并且如果在缓存响应之前想要修改这个响应,可以设置AFN的一个block,在这个block中修改响应信息,如下。(当然也可以在这个block中设置过滤,根据请求信息过滤掉不想缓存的响应)

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    [manager setDataTaskWillCacheResponseBlock:^NSCachedURLResponse * _Nonnull(NSURLSession * _Nonnull session, 
               NSURLSessionDataTask * _Nonnull dataTask, NSCachedURLResponse * _Nonnull proposedResponse) {
        //修改响应信息
        NSURLResponse *response = proposedResponse.response;
        NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse*)response;
        NSDictionary *headers = HTTPResponse.allHeaderFields;
        NSCachedURLResponse *cachedResponse;
        //这里就可以针对Cache-Control进行更改,然后直接我们通过某方法获取NSCachedURLResponse的时候就可以先去判断下头域信息
        NSMutableDictionary *modifiedHeaders = headers.mutableCopy;
        [modifiedHeaders setObject:@"max-age=1000" forKey:@"Cache-Control"];
        NSHTTPURLResponse *modifiedResponse = [[NSHTTPURLResponse alloc] initWithURL:HTTPResponse.URL statusCode:
                                                       HTTPResponse.statusCode HTTPVersion:@"HTTP/1.1" headerFields:modifiedHeaders];
        cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:modifiedResponse data:proposedResponse.data
                                                     userInfo:proposedResponse.userInfo storagePolicy:proposedResponse.storagePolicy];
                
        return cachedResponse;      //返回修改后的响应
    }];
    

    如果想要AFN使用已经保存好的响应,那么直接设置AFN的缓存策略允许使用缓存即可,几种缓存使用策略上面已经介绍过了

    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
    

    通常在实际应用中需要根据不同的网络状态设置不同的缓存策略,一般对于JSON数据来说,因为它通常是非资源文件,可能会经常变动,所以在有网的情况下需要禁用缓存来实时刷新,在无网的情况下才使用缓存数据。通常可以配合AFN的网络监听一起使用,以达到在不同的网络环境下使用不同的缓存策略的效果。

    //使用AFN框架来检测网络状态的改变
    -(void)AFNReachability{
        /*
         AFNetworkReachabilityStatusUnknown     = 未知
         AFNetworkReachabilityStatusNotReachable   = 没有网络
         AFNetworkReachabilityStatusReachableViaWWAN = 3G
         AFNetworkReachabilityStatusReachableViaWiFi = WIFI
         */
        [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
            switch (status) {
                case AFNetworkReachabilityStatusUnknown:
                    self.manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataDontLoad;
                    break;
                case AFNetworkReachabilityStatusNotReachable:
                    self.manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataDontLoad;
                    break;
                case AFNetworkReachabilityStatusReachableViaWWAN:
                    self.manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringCacheData;
                    break;
                case AFNetworkReachabilityStatusReachableViaWiFi:
                    self.manager.requestSerializer.cachePolicy = NSURLRequestReloadIgnoringCacheData;
                    break;
                default:
                    self.manager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataDontLoad;
                    break;
            }
        }];
        //开始监听
        [manager startMonitoring];
    }
    

    参考文章
    iOS网络——NSURLCache设置网络请求缓存
    NSURLSession发送请求通过NSURLCache 做的缓存
    NSURLSession 所有的都在这里(二)
    https://www.cnblogs.com/rainySue/p/huan-cun.html#toc_11

    相关文章

      网友评论

        本文标题:iOS缓存配置、AFN缓存配置

        本文链接:https://www.haomeiwen.com/subject/metucqtx.html