AFNetworking和离线模式下的SDURLCache不起作用

我正在使用AFNetworkingSDURLCache来处理所有的networking操作。

我有这样的SDURLCache设置:

 SDURLCache *urlCache = [[SDURLCache alloc] initWithMemoryCapacity:1024*1024*2 // 2MB mem cache diskCapacity:1024*1024*15 // 15MB disk cache diskPath:[SDURLCache defaultCachePath]]; [urlCache setMinCacheInterval:1]; [NSURLCache setSharedURLCache:urlCache]; 

我所有的请求都使用cachePolicy NSURLRequestUseProtocolCachePolicy ,根据苹果文档,它的工作原理是这样的:

如果请求中不存在NSCachedURLResponse,则从原始源获取数据。 如果请求存在caching响应,则URL加载系统检查响应以确定是否指定内容必须重新生效。 如果内容必须重新确认,则与原始源进行连接以查看是否已经改变。 如果没有改变,则从本地caching返回响应。 如果发生了变化,则从原始源获取数据。

如果caching的响应没有指定内容必须重新生效,则检查响应中指定的最大使用期限或到期date。 如果caching的响应足够近,则从本地caching中返回响应。 如果响应被确定为陈旧,则检查起源的新数据。 如果更新的数据可用,则从源发送源获取数据,否则从caching中返回。

所以,即使在飞机模式下,只要caching没有陈旧,一切都可以正常工作。 当caching过期(max-age等)时,失败块被调用。

我一直在SDURLCache里面挖掘一点,这个方法返回一个有效数据的响应(我把数据parsing成一个string,它包含caching的信息)

 - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request { request = [SDURLCache canonicalRequestForRequest:request]; NSCachedURLResponse *memoryResponse = [super cachedResponseForRequest:request]; if (memoryResponse) { return memoryResponse; } NSString *cacheKey = [SDURLCache cacheKeyForURL:request.URL]; // NOTE: We don't handle expiration here as even staled cache data is // necessary for NSURLConnection to handle cache revalidation. // Staled cache data is also needed for cachePolicies which force the // use of the cache. __block NSCachedURLResponse *response = nil; dispatch_sync(get_disk_cache_queue(), ^{ NSMutableDictionary *accesses = [self.diskCacheInfo objectForKey:kAFURLCacheInfoAccessesKey]; // OPTI: Check for cache-hit in in-memory dictionary before to hit FS if ([accesses objectForKey:cacheKey]) { response = [NSKeyedUnarchiver unarchiveObjectWithFile: [_diskCachePath stringByAppendingPathComponent:cacheKey]]; if (response) { // OPTI: Log entry last access time for LRU cache eviction // algorithm but don't save the dictionary // on disk now in order to save IO and time [accesses setObject:[NSDate date] forKey:cacheKey]; _diskCacheInfoDirty = YES; } } }); // OPTI: Store the response to memory cache for potential future requests if (response) { [super storeCachedResponse:response forRequest:request]; } return response; } 

所以在这一点上,我不知道该怎么做,因为我相信响应是由操作系统处理,然后AFNetworking接收

 - (void)connection:(NSURLConnection *)__unused connection didFailWithError:(NSError *)error 

AFURLConnectionOperation里面。

那么我终于达成了一个不那么丑陋的解决方法:

第一

如果您使用的是IOS5 / IOS6,则可以删除SDURLCache并使用本机:

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

但请记住,在IOS5 HTTPS请求不会caching在IOS6他们会。

第二

我们需要添加以下框架到我们的Prefix.pch ,AFNetworking可以开始监控我们的互联网连接。

 #import <MobileCoreServices/MobileCoreServices.h> #import <SystemConfiguration/SystemConfiguration.h> 

第三

我们需要和AFHTTPClient实例,所以我们可以拦截每个传出的请求并更改他的cachePolicy

 -(NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters { NSMutableURLRequest * request = [super requestWithMethod:method path:path parameters:parameters]; if (request.cachePolicy == NSURLRequestUseProtocolCachePolicy && self.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) { request.cachePolicy = NSURLRequestReturnCacheDataDontLoad; } if (self.networkReachabilityStatus == AFNetworkReachabilityStatusUnknown) { puts("uknown reachability status"); } return request; } 

通过这些代码,我们现在可以检测到wifi / 3G不可用,并指定无论如何都要使用caching的请求。 (离线模式)

笔记

  • AFNetworkReachabilityStatusUnknownAFNetworkReachabilityStatusUnknown时,我仍然不知道该怎么办。这可能发生在应用程序启动并且AF还没有获得互联网状态之后发出请求。

  • 请记住,为了使其工作,服务器必须在http响应中设置正确的caching标头。

UPDATE

看起来IOS6在没有互联网的情况下加载caching的响应有一些问题,所以即使请求被caching并且请求caching策略被设置为NSURLRequestReturnCacheDataDontLoad ,请求也会失败。

所以一个丑陋的解决方法是修改(void)connection:(NSURLConnection __unused *)connection didFailWithError:(NSError *)errorAFURLConnectionOperation.m检索caching的响应,如果请求失败,但只为特定的caching策略。

 - (void)connection:(NSURLConnection __unused *)connection didFailWithError:(NSError *)error { self.error = error; [self.outputStream close]; [self finish]; self.connection = nil; //Ugly hack for making the request succeed if we can find a valid non-empty cached request //This is because IOS6 is not handling cache responses right when we are in a no-connection sittuation //Only use this code for cache policies that are supposed to listen to cache regarding it's expiration date if (self.request.cachePolicy == NSURLRequestUseProtocolCachePolicy || self.request.cachePolicy == NSURLRequestReturnCacheDataElseLoad || self.request.cachePolicy == NSURLRequestReturnCacheDataDontLoad) { NSCachedURLResponse * cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request]; if (cachedResponse.data.length > 0) { self.responseData = cachedResponse.data; self.response = cachedResponse.response; self.error = nil; } } } 

如果没有你的HTTP头文件,就不能分辨出来 – 但是最常见的原因是NSURLProtocol在向WebView传递caching响应之前强制重新生效。

请看这里: http : //robnapier.net/blog/offline-uiwebview-nsurlprotocol-588

这听起来像你希望请求成功,即使caching说数据已经过期,应该从服务器检索。 您可能有一些运气设置某些请求的caching策略(线上与线下的不同策略),您希望使用陈旧的数据而不是失败。

NSMutableURLRequest – > setCachePolicy

它看起来像NSURLRequestReturnCacheDataDontLoad是你想要离线模式的策略。

希望有所帮助!