了解NSURLCahe

NSURLCache类用NSURLRequest对象和NSCachedURLResponse对象的一对一映射关系实现了请求数据的缓存。它同时提供内存缓存和硬盘缓存,你可以分别自定义内存缓存和硬盘缓存的大小,同时也可以自定义硬盘缓存的目录。

这是官方文档对NSURLCache的描述。其中NSURLRequest对象是请求对象,不必多说。NSCachedURLResponse对象是对缓存数据的封装,其中的data属性是请求回来的JSON(或者其他格式)的二进制数据。

以下是NSURLCache类提供的方法,基本能够满足大多数的缓存需求。

@interface NSURLCache : NSObject/** 缓存类的单例 */ @property (class, strong) NSURLCache *sharedURLCache; /** 初始化方法 */ - (instancetype)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(nullable NSString *)path; /** 取得缓存数据的方法 */ - (nullable NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request; /** 存储缓存数据的方法 */ - (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request; /** 删除指定request的缓存 */ - (void)removeCachedResponseForRequest:(NSURLRequest *)request; /** 删除全部缓存 */ - (void)removeAllCachedResponses; /** 删除缓存数据的一部分 */ - (void)removeCachedResponsesSinceDate:(NSDate *)date; /** 内存缓存的大小 单位:字节 */ @property NSUInteger memoryCapacity; /** 硬盘缓存的大小 单位:字节 */ @property NSUInteger diskCapacity; /** 当前可用的内存缓存大小 单位:字节 */ @property (readonly) NSUInteger currentMemoryUsage; /** 当前可用的硬盘缓存大小 单位:字节 */ @property (readonly) NSUInteger currentDiskUsage; @end

缓存工作过程的理解

事实上,就算什么也不写,系统也会根据默认的规则帮你缓存HTTP请求。但是项目中诸多的逻辑往往并不能让我们如此悠闲。

此处举一个小例子:项目中的请求一般都需要把参数加密,一般的加密算法,同样一个请求,每次加密出来的串都是不一样的。上面说过,NSURLCache是用NSURLRequest作为Key来实现缓存的,每次的URL不同导致每次取到的缓存都为空。这时候就需要做一些事情来保证缓存系统按照我们期望的样子正常运行。

我自己的理解和总结,NSURLCache的工作过程是这样的:

1.请求前的配置,包括请求头,响应头,超时时间以及缓存策略(后面会说到有关缓存策略)。

2.真正去服务器请求前,判断缓存策略,调用cachedResponseForRequest:(NSURLRequest *)request方法试着取缓存或者直接请求网络。

3.如果缓存策略允许取缓存,并且取到了缓存,请求成功并且返回缓存数据。

4.如果缓存策略允许取缓存,并且没有取到缓存,再次判断缓存策略,如果缓存策略允许联网,则联网请求,否则,请求失败。

5.上述2,3任何一种请求成功的话,判断缓存策略和服务器返回的响应头。如果允许存储,则调用storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request将返回的数据存储到内存以及硬盘,否则,直接返回请求成功。

下面是Apple提供的7种缓存策略以及含义:

如果使用了默认缓存策略,也就是上面表格中第一个,需要从返回的response的header中获取相应的字段来指导缓存该如何进行。

1.Cache-Control字段:常用的有 no-cache,no-store,和max-age。其中no-cache代表不能使用这个缓存,no-store代表不存储这个数据,max-age代表缓存的有效期(单位为秒)。

2.Expires字段:缓存过期时间,后面跟一个日期,此日期之前都可以直接使用本缓存。如果Expires与Cache-Control同时存在,则Cache-Control优先。

3.Last-Modified和If-Modified-Since字段:如果response中有Last-Modified,则在下次请求时,给request的header设置If-Modified-Since为Last-Modified的值,服务器校验数据是否有变化,如果有变化,返回新数据,否则,返回304状态码,可以使用此缓存。

4.ETag和If-None-Match字段:如果response中有ETag,则在下次请求时,给request的header设置If-None-Match为ETag的值,服务器校验数据是否有变化,如果有变化,返回新数据,否则,返回304状态码,可以使用此缓存。

以上的缓存协议字段只是我所了解的比较常见的几种,当然HTTP缓存协议还包括很多很多的内容,有兴趣的同学可以自行了解。

Demo应用

为了加深理解,我写了一个小Demo来探索NSURLCache的运行过程。

Demo很简单,只有WXYRequest请求类,继承自NSURLCache的自定义WXYURLCache类和发起请求的ViewController控制器。

第一步、配置自定义缓存类。

    //配置缓存NSUInteger memoryCapacity = 20*1024*1024; NSUInteger diskCapacity = 50*1024*1024; WXYURLCache *customURLCache = [[WXYURLCache alloc] initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:[WXYURLCache customCachePath]]; [NSURLCache setSharedURLCache:customURLCache];

设置了20M的内存缓存和50M的硬盘缓存。以及自定义的缓存目录。这是一个单例,设置了之后,整个工程里走系统缓存的请求都会遵循这个设置。此处需要注意一点,自定义的目录只需要设置一个目录名即可,它会自动存到应用程序沙盒的Caches目录下,不需要手动获取Caches目录。

+ (NSString *)customCachePath{return @"CustomCache";
}

第二步、设置请求,这里使用了AFNetworking。

+ (void)requestWithSuccess:(SuccessBlock)success failure:(failureBlock)failure{AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager];//配置请求头sessionManager.requestSerializer = [AFHTTPRequestSerializer serializer];sessionManager.requestSerializer.cachePolicy = [self getCachePolicy];//缓存策略//配置响应头 sessionManager.responseSerializer = [AFJSONResponseSerializer serializer]; [sessionManager GET:customURLString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSLog(@"\n请求成功:\n \nURL:%@\n \nresponse:%@\n\n", task.currentRequest.URL.absoluteString, responseObject); success(responseObject); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"\n请求失败: \n \nURL:%@\n \nerror:%@\n\n", task.currentRequest.URL.absoluteString, [error.userInfo objectForKey:@"NSLocalizedDescription"]); failure(error); }]; }

其中的缓存策略,每种都试了一遍。

+ (NSURLRequestCachePolicy)getCachePolicy{NSURLRequestCachePolicy cachePolicy;/** 根据后台返回的响应头来做判断如何缓存 *///cachePolicy = NSURLRequestUseProtocolCachePolicy;/** 每次刷新,不取缓存 */ //cachePolicy = NSURLRequestReloadIgnoringLocalCacheData; /** 有缓存取缓存,无缓存请求 */ cachePolicy = NSURLRequestReturnCacheDataElseLoad; /** 有缓存取缓存,无缓存返回失败 */ //cachePolicy = NSURLRequestReturnCacheDataDontLoad; return cachePolicy; }

第三步、重写NSURLCache的方法。

重写取缓存方法。

- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request{NSCachedURLResponse *cachedURLResponse = [super cachedResponseForRequest:request];id cacheData = nil; if (!cachedURLResponse.data) { cacheData = @"取到的缓存为空"; } else{ cacheData = [NSJSONSerialization JSONObjectWithData:cachedURLResponse.data options:NSJSONReadingMutableContainers error:nil]; } NSLog(@"\n取缓存:\n \nURL:%@\n \nresponse:%@\n\n", request.URL.absoluteString, cacheData); return cachedURLResponse; }

重写存缓存方法。

- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request{id cacheData = [NSJSONSerialization JSONObjectWithData:cachedResponse.data options:NSJSONReadingMutableContainers error:nil]; NSLog(@"\n存缓存:\n \nURL:%@\n \nresponse:%@\n\n", request.URL.absoluteString, cacheData); [super storeCachedResponse:cachedResponse forRequest:request]; }

最后一步、请求数据看控制台输出。

这里使用了一个获取域名资质信息的免费接口。

#define customURLString @"http://www.sojson.com/api/beian/baidu.com"

发起请求,以NSURLRequestReturnCacheDataElseLoad(有缓存取缓存,无缓存请求)缓存策略为例。

/** 发起请求 */
- (IBAction)WXY_Requet:(id)sender {[WXYRequest requestWithSuccess:^(id response) {} failure:^(NSError *error) { }]; }

可以看到控制台的输出是这样的:

2017-03-20 23:12:23.425763 WXYURLChache[530:99758] 取缓存: URL:http://www.sojson.com/api/beian/baidu.com response:取到的缓存为空 2017-03-20 23:12:24.094012 WXYURLChache[530:99758] 存缓存: URL:http://www.sojson.com/api/beian/baidu.com response:{ checkDate = ""; domain = " baidu.com "; icp = "\U4eacICP\U8bc1030173\U53f7"; indexUrl = "www.baidu.com"; name = "\U5317\U4eac\U767e\U5ea6\U7f51\U8baf\U79d1\U6280\U6709\U9650\U516c\U53f8"; nature = "\U4f01\U4e1a"; nowIcp = "\U4eacICP\U8bc1030173\U53f7-1"; search = "baidu.com"; sitename = "\U767e\U5ea6"; type = 200; } 2017-03-20 23:12:24.095622 WXYURLChache[530:99560] 请求成功: URL:http://www.sojson.com/api/beian/baidu.com response:{ checkDate = ""; domain = " baidu.com "; icp = "\U4eacICP\U8bc1030173\U53f7"; indexUrl = "www.baidu.com"; name = "\U5317\U4eac\U767e\U5ea6\U7f51\U8baf\U79d1\U6280\U6709\U9650\U516c\U53f8"; nature = "\U4f01\U4e1a"; nowIcp = "\U4eacICP\U8bc1030173\U53f7-1"; search = "baidu.com"; sitename = "\U767e\U5ea6"; type = 200; }

首先根据缓存策略取缓存,因为是第一次请求,没有缓存。然后联网请求,将请求回来的数据存入缓存。最后返回请求成功。通过Charles抓包抓到了一个HTTP请求。

这时什么也不做,发起第二次同样的的请求,可以看到这时的控制台输出变成了这样:

2017-03-20 23:19:29.117268 WXYURLChache[530:99619] 取缓存: URL:http://www.sojson.com/api/beian/baidu.com response:{ checkDate = ""; domain = " baidu.com "; icp = "\U4eacICP\U8bc1030173\U53f7"; indexUrl = "www.baidu.com"; name = "\U5317\U4eac\U767e\U5ea6\U7f51\U8baf\U79d1\U6280\U6709\U9650\U516c\U53f8"; nature = "\U4f01\U4e1a"; nowIcp = "\U4eacICP\U8bc1030173\U53f7-1"; search = "baidu.com"; sitename = "\U767e\U5ea6"; type = 200; } 2017-03-20 23:19:29.120207 WXYURLChache[530:100693] 存缓存: URL:http://www.sojson.com/api/beian/baidu.com response:{ checkDate = ""; domain = " baidu.com "; icp = "\U4eacICP\U8bc1030173\U53f7"; indexUrl = "www.baidu.com"; name = "\U5317\U4eac\U767e\U5ea6\U7f51\U8baf\U79d1\U6280\U6709\U9650\U516c\U53f8"; nature = "\U4f01\U4e1a"; nowIcp = "\U4eacICP\U8bc1030173\U53f7-1"; search = "baidu.com"; sitename = "\U767e\U5ea6"; type = 200; } 2017-03-20 23:19:29.123088 WXYURLChache[530:99560] 请求成功: URL:http://www.sojson.com/api/beian/baidu.com response:{ checkDate = ""; domain = " baidu.com "; icp = "\U4eacICP\U8bc1030173\U53f7"; indexUrl = "www.baidu.com"; name = "\U5317\U4eac\U767e\U5ea6\U7f51\U8baf\U79d1\U6280\U6709\U9650\U516c\U53f8"; nature = "\U4f01\U4e1a"; nowIcp = "\U4eacICP\U8bc1030173\U53f7-1"; search = "baidu.com"; sitename = "\U767e\U5ea6"; type = 200; }

这次取缓存的方法取到了缓存,同样将数据存储了一次。然后返回请求成功。

通过Charles抓包没有抓到任何请求。说明这次并没有联网请求,而是根据缓存策略直接使用的缓存。

把手机打开飞行模式,再次发起请求。可以看到控制台的输出和上面是一样的。说明取缓存的策略下,即使是没有网络,也会返回请求成功。

最后一次实验,飞行模式打开,清除掉缓存。

/** 清空缓存 */
- (IBAction)WXY_RemoveCache:(id)sender {[[NSURLCache sharedURLCache] removeAllCachedResponses];
}

这时发起请求,可以看到控制台的输出是这样:

2017-03-20 23:28:37.618673 WXYURLChache[530:101997] 取缓存: URL:http://www.sojson.com/api/beian/baidu.com response:取到的缓存为空 2017-03-20 23:28:37.646091 WXYURLChache[530:99560] 请求失败: URL:http://www.sojson.com/api/beian/baidu.com error:The Internet connection appears to be offline.

首先根据缓存策略取缓存,取不到缓存,联网请求,无网络请求失败之后,并不会调用存储缓存的方法,直接返回请求失败。

以上只是探讨了NSURLRequestReturnCacheDataElseLoad这一种缓存策略的表现情况。其他策略,限于篇幅,不做赘述。有兴趣的同学可以自行试验以加深理解。

写在最后

因为最近要替换掉项目中基于ASI的网络框架,改用AFN。在封装的过程中,顺便研究了一下缓存相关,觉得有必要记录一下,分享给大家,所以写了这篇文章。

另外,值得注意的是,ASI并不是基于NSURLSession或者NSURLCOnnection的封装,所以并不会走NSURLCache的缓存,它有自己的一套缓存系统。只有NSURLSession或者NSURLCOnnection的请求才会走Apple提供的这个缓存类。

还有,看了一下缓存的沙盒目录,NSURLCache通过数据库来实现存储缓存。

最后,像前面说的,并不是有了这个类,就可以不去管缓存的事情了,根据项目架构和需求的不同,在NSURLCache之上需要做的还有很多很多。

来自:http://www.cocoachina.com/ios/20170322/18934.html

转载于:https://www.cnblogs.com/fakeCoder/p/5093724.html

URLCache探索相关推荐

  1. 探索 TVM 进行量化方法

    探索 TVM 进行量化方法 Relay框架 如上图所示,有两种不同的并行工作正在进行中 • 自动整数量化 - 采用 FP32 框架图,在 Relay 中自动转换为 Int8. • 接受预量化整数模型 ...

  2. 自主数据类型:在TVM中启用自定义数据类型探索

    自主数据类型:在TVM中启用自定义数据类型探索 介绍 在设计加速器时,一个重要的决定是如何在硬件中近似地表示实数.这个问题有一个长期的行业标准解决方案:IEEE 754浮点标准.1.然而,当试图通过构 ...

  3. 2021年大数据ELK(二十六):探索数据(Discovery)

    全网最详细的大数据ELK文章系列,强烈建议收藏加关注! 新文章都已经列出历史文章目录,帮助大家回顾前面的知识重点. 目录 探索数据(Discovery) 一.使用探索数据功能 二.导入更多的Apach ...

  4. Python数据挖掘:数据探索,数据清洗,异常值处理

    来源:天善智能韦玮老师 课堂笔记 作者:Dust 探索性数据分析Exploratory Data Analysis,EDA 数据探索的核心是︰ 1.数据质量分析(跟数据清洗密切联系) 2.数据特征分析 ...

  5. oracle无创建directory权限,【DIRECTORY】普通用户创建Oracle DIRECTORY数据库对象的权限需求及探索...

    可能遇到的报错信息如若在创建DIRECTORY之前普通用户sec未获得相应权限,将会收到最为常见的"ORA-01031: insufficient privileges"错误.模拟 ...

  6. sources root pycharm 怎么设置_使用python语言开发ROOT之搭建环境方法探索

    这里指的是使用python调用ROOT的模块,编写程序用来处理数据的环境搭建方法. 经过研究,一般有四种方案 环境变量法,主要依靠ROOT本身,环境变量是关键 预编译包,但是该方法没有证实成功搭建py ...

  7. 论计算机的创新性,论计算机专业教学创新探索

    1/4论计算机专业教学创新探索改革高校计算机专业实训课程教学方法的重要意义高校计算机专业实训课程是计算机专业的实践课程,在计算机专业课程体系中处于非常重要的位置,主要用于培养计算机专业学生实践应用能力 ...

  8. mysql 集群实践_MySQL Cluster集群探索与实践

    MySQL集群是一种在无共享架构(SNA,Share Nothing Architecture)系统里应用内存数据库集群的技术.这种无共享的架构可以使得系统使用低廉的硬件获取高的可扩展性. MySQL ...

  9. php内核探索方法与资源

    PHP内核探索 TIPI深入理解PHP内核 风雪之隅PHP源码分析 <php扩展开发及内核应用> 百度XLQ God's blog codinglabs PHP内核探索:从SAPI接口开始 ...

最新文章

  1. 防止办公用计算机,关于加强办公室计算机使用管理的规定
  2. SXOI2019游记
  3. 如何:在 Visual Studio 中添加或移除引用(转载)
  4. OpenCV使用模板与遮罩匹配的实例(附完整代码)
  5. Java中的守护线程和非守护线程(转载)
  6. 模型OnMouseXXX事件
  7. 听说做开发的工资随随便便就上10k?
  8. Java笔记(四)各类容器,set,map,队列实现
  9. linux shell 特殊符号的表示
  10. Activity管理类,随时随地退出应用程序
  11. 大一计算机引论知识点,计算机引论知识点2015精选.doc
  12. [Mac]『Grid』(格子) 免费又实用的分屏软件
  13. python中numpy下载问题
  14. SO、SOP、SOIC、MSOP、TSSOP、TSOP、VSSOP、SSOP、SOJ封装详解
  15. lcms质谱仪_常用质谱的优缺点_液质联用(LCMS)仪器社区_仪器信息网论坛
  16. Android系统应用---SystemUI之三:状态栏电池图标的显示和Android电池管理的探讨
  17. js 小写金额转大写
  18. 基于3D Vision眼镜的OSG立体显示 【转】
  19. PTA】{10-1 结构拓展实验,函数}6-3 求某学生8门课的平均分 (10 分)
  20. cas1584803-67-7|花菁染料齐岳试剂

热门文章

  1. 【Tools】MarkDown教程(一)-MarkDown简介
  2. react取消捕获_React 面试指南 (上)
  3. 1006 换个格式输出整数 (15 分)(c语言)
  4. 防火墙(9)——禁止某个时间段内访问我们的web
  5. 正则表达式—leetcode10
  6. 验证二叉搜索数—leetcode98
  7. CF-1209 F. Koala and Notebook(建图BFS)
  8. GDB多线程调试常用命令
  9. Leetcode题库 144.二叉树的前序遍历(递归 C实现)
  10. [转] c++的多态(一个接口,多种实现)