NSURLCache

Posted by LML on March 23, 2020

1.前言

本文主要介绍NSURLCache的相关内容,NSCache的相关介绍见上篇

2.简介

The NSURLCache class implements the caching of responses to URL load requests by mapping NSURLRequest objects to NSCachedURLResponse objects. It provides a composite in-memory and on-disk cache, and lets you manipulate the sizes of both the in-memory and on-disk portions. You can also control the path where cache data is stored persistently.

NSURLCache 用于进行请求数据的缓存,提供磁盘+内存缓存。 response是通过请求的url+参数来作为key储存的

3.缓存策略

当使用 NSURLSession 时 , 我们可以直接通过 NSMutableURLRequest 来指定 NSURLRequestCachePolicy .
官方API:

+(instancetype)requestWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval;

typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy) {
NSURLRequestUseProtocolCachePolicy = 0, //默认策略
NSURLRequestReloadIgnoringLocalCacheData = 1, //忽略缓存,必须从远程地址下载;
NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented 未实现
NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
NSURLRequestReturnCacheDataElseLoad = 2, // 无论缓存是否过期 , 有就使用缓存 , 没有就请求数据.
NSURLRequestReturnCacheDataDontLoad = 3,// 无论缓存是否过期 , 有就使用缓存 , 没有就请求失败.
NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented 未实现 }

- (void)example_1{
    NSURL *url = [NSURL URLWithString:@"http://via.placeholder.com/50x50.jpg"];
    //默认使用 HTTP缓存策略来进行缓存
     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (error) {
            NSLog(@"error warning : %@",error);
        }else{
            //从缓存当中读取数据!
            NSData *tempData = data;
            NSString *responseStr = [[NSString alloc] initWithData:tempData encoding:NSUTF8StringEncoding];
            NSLog(@"response:%@",response);
        }
    }] resume];
    
}

上面所谓的默认策略即遵循 HTTP Header 中的缓存策略:在 HTTP 中,控制缓存开关的字段有两个:Pragma 和 Cache-Control . Pragma 是旧产物,已经逐步抛弃,有些网站为了向下兼容还保留了这两个字段. 在请求中使用 Cache-Control 时,它可选的值有:

3.1 缓存校验

在缓存中,我们需要一个机制来验证缓存是否有效。比如服务器的资源更新了,客户端需要及时刷新缓存;又或者客户端的资源过了有效期,但服务器上的资源还是旧的,此时并不需要重新发送。缓存校验就是用来解决这些问题的。服务端返回的response 会带有两个字段 Last-Modified 和 etag

  • Last-Modified :服务端在返回资源时,会将该资源的最后更改时间通过 Last-Modified 字段返回给客户端。
    • 客户端下次请求时通过 If-Modified-Since 或者 If-Unmodified-Since 带上 Last-Modified ,服务端检查该时间是否与服务器的最后修改时间一致:
      • 如果一致,则返回 304 状态码,不返回资源;
      • 如果不一致则返回 200 和修改后的资源,并带上新的时间。
    • If-Modified-Since 和 If-Unmodified-Since 的区别是:
      • If-Modified-Since:告诉服务器如果时间一致,返回状态码 304
      • If-Unmodified-Since:告诉服务器如果时间不一致,返回状态码 412
  • etag:单纯的以修改时间来判断还是有缺陷,比如文件的最后修改时间变了,但内容没变。对于这样的情况,我们可以使用 Etag 来处理。服务器通过某个算法对资源进行计算,取得一串值(类似于文件的 hash 值),之后将该值通过 Etag 返回给客户端。
    • 客户端下次请求时通过 If-None-Match 或 If-Match 带上该值,服务器对该值进行对比校验:如果一致则不要返回资源。

3.2 浏览器加载HTTP缓存机制流程

从整个流程来看 , 他们如下图:

3.3 HTTP 缓存内容查看

打开沙盒路径下的Library/Caches 中,你可以看到缓存文件: 本次 HTTP 请求信息、Response信息都在各个表中存储完毕.

3.4 平时我们应用

默认就已经设置好了512kb的内存缓存空间,以及10MB的磁盘缓存空间。可能你的代码中并没有写任何与NSURLCache有关的东西,但其实它已经默默的开始帮你进行缓存了。

print(NSURLCache.sharedURLCache().diskCapacity)
//output:
//10000000

print(NSURLCache.sharedURLCache().memoryCapacity)
//output:
//512000
使用NSURLCache之前需要在AppDelegate中缓存空间的设置:  

我们也可自己进行设置缓存的大小

- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {  

  NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024  
                                                       diskCapacity:20 * 1024 * 1024  
                                                           diskPath:nil];  
  [NSURLCache setSharedURLCache:URLCache];  }     

遵守默认设置或者自定义设置之后,我们通过NSURLSession 发送请求时,可以使用使用NSMutableURLRequest 默认的缓存策略,也可以使用其他的缓存策略

4. 缓存原理

NSURLCache 默认进行了内存+磁盘缓存,但是具体如何进行内存和磁盘缓存的相关介绍比较少,需要阅读源码,后期阅读源码就补充本节内容。但是猜测如下:

  • 内存缓存 :NSCache
  • 磁盘缓存:数据库+文件

5. 参考

https://juejin.im/post/5d76162bf265da03b120738d#heading-12 https://www.jianshu.com/p/aa49bb3555f4