我正在使用AFNetworking
和SDURLCache
进行所有网络操作。
我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
,根据apple docs的说法是这样的:
如果请求中不存在NSCachedURLResponse,那么 数据从始发源获取。如果有缓存 对请求的响应,URL加载系统检查响应 确定它是否指定必须重新验证内容。如果 必须重新验证内容,并建立连接 原始来源,看它是否已经改变。如果它没有改变, 然后从本地缓存返回响应。如果它改变了, 数据从原始来源获取。
如果缓存的响应未指定必须重新验证内容,则响应中指定的最长期限或到期时间 检查。如果缓存的响应是最近的,那么 响应从本地缓存返回。如果回应是 确定是陈旧的,检查原始来源是否更新 数据。如果有更新的数据,则从中获取数据 原始来源,否则从缓存中返回。
因此,只要缓存不是陈旧的,即使在飞行模式下一切都能正常工作。当缓存过期(max-age和其他)时,将调用失败块。
我一直在SDURLCache
内部挖掘,这个方法返回一个有效数据的响应(我已经将数据解析为一个字符串,它包含缓存的信息)
- (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
内。
答案 0 :(得分:12)
好吧,我终于达成了一个不那么丑陋的解决方法:
<强>第一强>
如果你正在使用IOS5 / IOS6,你可以删除SDURLCache并使用原生的:
//Set Cache
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
diskCapacity:20 * 1024 * 1024
diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
但请记住,在IOS5中,https请求不会在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何时不可用,并指定始终使用缓存的请求,无论如何。 (离线模式)
备注强>
当networkReachabilityStatus
为AFNetworkReachabilityStatusUnknown
时,我仍然不知道该怎么做。这可能发生在应用程序启动并且AF未获得互联网状态后立即发出请求爱好。
请记住,为了使其正常工作,服务器必须在http响应中设置正确的缓存标头。
<强>更新强>
看起来IOS6在非互联网情况下加载缓存响应时遇到一些问题,因此即使请求被缓存且请求缓存策略设置为NSURLRequestReturnCacheDataDontLoad
,请求也会失败。
因此,一个丑陋的解决方法是修改(void)connection:(NSURLConnection __unused *)connection
didFailWithError:(NSError *)error
中的AFURLConnectionOperation.m
以在请求失败时检索缓存的响应,但仅针对特定的缓存策略。
- (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;
}
}
}
答案 1 :(得分:0)
没有HTTP标头也说不清楚 - 但最常见的原因是NSURLProtocol
在向WebView提供缓存响应之前强制重新验证。
请看这里: http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588
答案 2 :(得分:0)
听起来您希望请求成功,即使缓存显示数据已过期且应从服务器检索。您可能有幸为某些请求设置缓存策略(在线与离线的不同策略),您宁愿使用陈旧数据而不是失败。
NSMutableURLRequest -> setCachePolicy
NSURLRequestReturnCacheDataDontLoad
似乎是离线模式所需的政策。
希望有所帮助!