ASIHttpRequest缓存无效分析

来源:互联网 发布:南京行知中学升学率 编辑:程序博客网 时间:2024/05/17 01:24

刚接触IOS一个月,正在做一个项目,需要频繁请求服务器接口获取数据。为了降低用户手机流量消耗,希望在请求数据不变的情况下,使用本地缓存。

ASIHttpRequest组件有提供缓存设置。

首先设置缓存策略,如果有缓存,且缓存不过期,且和服务器版本一致则使用缓存,否则请求网络,无网络时使用缓存:

[request setCachePolicy:ASIAskServerIfModifiedWhenStaleCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];
然后设置缓存:
[request setDownloadCache:appDelegate.myCache];
设置缓存存储方式,永久保存在本地:
[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];

以上设置好之后,在requestFinished中加上输出语句查看是否使用缓存:

NSLog(@"%@", [request didUseCachedResponse]?@"yes":@"no");
结果发现,无论怎么试,除非关闭网络,否则永远都是no,也就是没有使用缓存。问题在哪呢?我们看一下缓存策略ASIAskServerIfModifiedWhenStaleCachePolicy的描述:request会先判断是否存在缓存数据。a, 如果没有再进行网络请求。 b,如果存在缓存数据,并且数据没有过期,则使用缓存。c,如果存在缓存数据,但已经过期,request会先进行网络请求,判断服务器版本与本地版本是否一样,如果一样,则使用缓存。如果服务器有新版本,会进行网络请求,并更新本地缓存

有网络、且数据无变化,仍然不使用缓存,因此推断缓存是否已过期。跟踪了ASIDownloadCache代码

if ([self shouldRespectCacheControlHeaders]) {// Look for X-ASIHTTPRequest-Expires header to see if the content is out of dateNSNumber *expires = [cachedHeaders objectForKey:@"X-ASIHTTPRequest-Expires"];if (expires) {if ([[NSDate dateWithTimeIntervalSince1970:[expires doubleValue]] timeIntervalSinceNow] >= 0) {[[self accessLock] unlock];return YES;}}// No explicit expiration time sent by the server[[self accessLock] unlock];return NO;}
发现这里的expires是空,也就是没有设置缓存过期时间。组件并没有设置默认的缓存过期时间

加上缓存过期时间设置:

[request setSecondsToCache:60*60*24*30];
测试一下,果然重复请求时都返回yes了。这就行了么?Noooooooooop,测试数据发生变化时,缓存仍然被使用了,这不是我们想要看到的。问题出在哪呢?

重新看一遍缓存策略描述:如果存在缓存数据,但已经过期,request会先进行网络请求,判断服务器版本与本地版本是否一样,如果一样,则使用缓存

显然缓存并没有过期,因此就不会请求网络。而我们的目标是:不管缓存是否过期,只要数据发生变化,就不使用缓存。因此调整我们的缓存策略,重新设置为:

[request setCachePolicy:ASIAskServerIfModifiedCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];

ASIAskServerIfModifiedCachePolicy的描述为:ASIAskServerIfModifiedWhenStaleCachePolicy大致一样,区别仅是每次请求都会 去服务器判断是否有更新

再次测试,仍然不请求网络,即使数据发生变化,还是会使用缓存。是不是要崩溃了。。。。。偷笑结合缓存策略描述,我们推断,是不是在和缓存和服务器版本比较的时候出了问题,错误的认为两个版本一致从而导致这个问题。那么ASIHttpRequest是如何比较呢?继续跟踪代码:

// New content is not differentif ([request responseStatusCode] == 304) {[[self accessLock] unlock];return YES;}// If we already have response headers for this request, check to see if the new content is different// We check [request complete] so that we don't end up comparing response headers from a redirection with theseif ([request responseHeaders] && [request complete]) {// If the Etag or Last-Modified date are different from the one we have, we'll have to fetch this resource againNSArray *headersToCompare = [NSArray arrayWithObjects:@"Etag",@"Last-Modified",nil];for (NSString *header in headersToCompare) {if (![[[request responseHeaders] objectForKey:header] isEqualToString:[cachedHeaders objectForKey:header]]) {[[self accessLock] unlock];return NO;}}}
从以上代码可以看出,首先检查reponseStatusCode是否等于304。相等则使用缓存,否则当request请求完成后,对返回头里的【ETag】和【Last-Modified】与本地缓存比较,如果不一致则认为数据变化,请求网络。断点调试发现,responseStatusCode始终是200,而返回头里并没有【Etag】和【CSDN:CODE:Last-Modified】这两个键值。那么这几个东西是从何而来呢?

百度一下Etag,发现一篇文章:HTTP缓存ETAG和Last-Modified 文章中提到: 在第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记此文件在服务期端最后被修改的时间。客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since 报头,询问该时间之后文件是否有被修改过。如果服务器端的资源没有变化,则自动返回 HTTP 304 (Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。ETag是一个可以与Web资源关联的记号(token)。

而从断点调试时打印返回头中并没有这两个键值可知,这是服务器端没有做客户端缓存处理。那么接下来,这就是服务器的事情了,我们的工作到此结束。明天上班去骚扰我们的服务器后台开发。。。。大笑

0 0
原创粉丝点击