缓存策略

缓存策略

我们做缓存的目的 :

  • 没有网络的时候,读取硬盘中的缓存数据 加载上次瞬间的数据
  • 在网络不好,下载速度慢的时候,避免空白页,先读取缓存,下载后更新数据 。
更具需要,需要做的缓存就是磁盘缓存 。需要数据之前,判断网络 。读取磁盘中上一次打开app的缓存 。

选取缓存技术

针对的功能的单一 。使用 NSURLCache

启用系统缓存

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

需要的接口 :

  • 判断缓存文件是否存在 如果没有创建
  • 文件中是否有缓存内容 如果没有加载当前瞬间的数据到缓存文件
  • 读取缓存路径中文件的缓存
  • 清空缓存

实现

  • 使用get请求
  • 设置内存缓存大小 、磁盘缓存大笑 、缓存路径 。

判断缓存的时效性

服务器的文件存贮,大多采用资源变动后就重新生成一个链接的做法。
但同时也不排除不同文件使用同一个链接。那么如果服务端的file更改了,本地已经有了缓存。如何更新缓存?

  • Last-Modified
    顾名思义,是资源最后修改的时间戳,往往与缓存时间进行对比来判断缓存是否过期
-  在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记此文件在服务期端最后被修改的时间,格式类似这样:

Last-Modified: Fri, 12 May 2006 18:53:33 GMT
- 客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since 报头,询问该时间之后文件是否有被修改过:
If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT
- 如果服务器端的资源没有变化,则自动返回 HTTP 304 (Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。

  • ETag

    ETag是一个可以与Web资源关联的记号(token)。它是一个 hash 值,用作 Request 缓存请求头,每一个资源文件都对应一个唯一的 ETag 值,服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:
    ETag: "50b1c1d4f775c61:df3"
    客户端的查询更新格式是这样的:
    If-None-Match: W/"50b1c1d4f775c61:df3"

那么判断规则就是这样的 :

            if LastModifiedFromServer != LastModifiedOnClien
             GetFromServer      
            else
             GetFromCache
/*!
 @brief 如果本地缓存资源为最新,则使用使用本地缓存。如果服务器已经更新或本地无缓存则从服务器请求资源。
 
 @details
 
 步骤:
 1. 请求是可变的,缓存策略要每次都从服务器加载
 2. 每次得到响应后,需要记录住 LastModified
 3. 下次发送请求的同时,将LastModified一起发送给服务器(由服务器比较内容是否发生变化)
 
 @return 图片资源
 */
- (void)getData:(GetDataCompletion)completion {
    NSURL *url = [NSURL URLWithString:kLastModifiedImageURL];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:15.0];
    
    //    // 发送 etag
    //    if (self.etag.length > 0) {
    //        [request setValue:self.etag forHTTPHeaderField:@"If-None-Match"];
    //    }
    // 发送 LastModified
    if (self.localLastModified.length > 0) {
        [request setValue:self.localLastModified forHTTPHeaderField:@"If-Modified-Since"];
    }
    
    [[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        
        // NSLog(@"%@ %tu", response, data.length);
        // 类型转换(如果将父类设置给子类,需要强制转换)
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSLog(@"statusCode == %@", @(httpResponse.statusCode));
        // 判断响应的状态码是否是 304 Not Modified (更多状态码含义解释: https://github.com/ChenYilong/iOSDevelopmentTips)
        if (httpResponse.statusCode == 304) {
            NSLog(@"加载本地缓存图片");
            // 如果是,使用本地缓存
            // 根据请求获取到`被缓存的响应`!
            NSCachedURLResponse *cacheResponse =  [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
            // 拿到缓存的数据
            data = cacheResponse.data;
        }
        
        // 获取并且纪录 etag,区分大小写
        //        self.etag = httpResponse.allHeaderFields[@"Etag"];
        // 获取并且纪录 LastModified
        self.localLastModified = httpResponse.allHeaderFields[@"Last-Modified"];
        //        NSLog(@"%@", self.etag);
        NSLog(@"%@", self.localLastModified);
        dispatch_async(dispatch_get_main_queue(), ^{
            !completion ?: completion(data);
        });
    }] resume];
}

NSURLRequest默认的cache policy是NSURLRequestUseProtocolCachePolicy, 是最能保持一致性的协议。

NSURLRequestReloadIgnoringCacheData 忽略缓存直接从原始地址下载

NSURLRequestReturnCacheDataElseLoad 只有在cache中不存在data时才从原始地址下载

NSURLRequestReturnCacheDataDontLoad 允许app确定是否要返回cache数据,如果使用这种协议当本地不存在response的时候,创建NSURLConnection or NSURLDownload实例时将会马上返回nil;这类似于离线模式,没有建立网络连接.

总结

ETag 是的功能与 Last-Modified 类似:服务端不会每次都会返回文件资源。客户端每次向服务端发送上次服务器返回的 ETag 值,服务器会根据客户端与服务端的 ETag 值是否相等,来决定是否返回 data,同时总是返回对应的 HTTP 状态码。客户端通过 HTTP 状态码来决定是否使用缓存。比如:服务端与客户端的 ETag 值相等,则 HTTP 状态码为 304,不返回 data。服务端文件一旦修改,服务端与客户端的 ETag 值不等,并且状态值会变为200,同时返回 data。

在官方给出的文档中提出 ETag 是首选的方式,优于 Last-Modified 方式。因为 ETag 是基于 hash ,hash 的规则可以自己设置,而且是基于一致性,是“强校验”。 Last-Modified 是基于时间,是弱校验,弱在哪里?比如说:如果服务端的资源回滚客户端的 Last-Modified 反而会比服务端还要新。

--
虽然 ETag 优于 Last-Modified ,但并非所有服务端都会支持,而 Last-Modified 则一般都会有该字段。 大多数情况下需要与服务端进行协调支持 ETag ,如果协商无果就只能退而求其次。

判断服务器是否支持etag
[[self.response allHeaderFields] valueForKey:@"ETag"]

思路
为资源分派 hash 值,然后对比服务端与本地缓存是否一致来决定是否需要更新缓存。

这种思路,在开发中经常使用,比如:处于安全考虑,登陆操作一般不会传输账号密码,而是传输对应的 hash 值-- token ,这里的 token 就可以看做一个 file 资源,如果想让一个用户登陆超时时间是三天,只需要在服务端每隔三天更改下 token 值,客户端与服务端值不一致,然后服务端返回 token 过期的提示。

注意
如果借助了Last-ModifiedETag,那么缓存策略则必须使用NSURLRequestReloadIgnoringCacheData 策略,忽略缓存,每次都要向服务端进行校验。

NSURLCache不能满足的情况
系统帮我们做的缓存,好处是自动,无需我们进行复杂的设置。坏处也恰恰是这个:不够灵活,不能自定义。只能指定一个缓存的总文件夹,不能分别指定每一个文件缓存的位置,更不能为每个文件创建一个文件夹,也不能指定文件夹的名称。缓存的对象也是固定的:只能是 GET请求的返回值。

瞬间的大图和缩略图缓存问题

要求 :使用同一个接口 ,在请求头中设置thumbnail 参数来区分 。

SDWebImageManager *sdmanger = [SDWebImageManager sharedManager];
[sdmanger.imageDownloader setValue:@"true" forHTTPHeaderField:@"thumbnail"]; 

通过上边给请求设置参数

[imageView sd_setImageWithURL:@"" placeholderImage:@"" options:SDWebImageHandleCookies|SDWebImageRefreshCached];

在下载图片的时候 ,通过options来控制下载方式 ,因为我们需要额外添加cookes 。所以需要options:SDWebImageDownloaderHandleCookies
另外需要注意的是 ,sdwebimage默认的是根据url缓存图片 ,所以因为两次请求的url是一样的 ,第二次发送请求高清图的时候并不会重新发送请求,而是从 缓存中提取了第一次的缩略图 。
所以,options 的选项中还要额外加一个SDWebImageRefreshCached
这样两次请求 。并不会有两份缓存 。在请求缩略图的时候 ,禁止重复请求 。保证不会让缩略图覆盖掉高清图 。

sdwebimage 的缓存库是用url 做一次运算以后作为key 。value 是图片的磁盘地址 。地址是用url 和 图片名称 用 md5 提取摘 。

你可能感兴趣的:(缓存策略)