我开发了一个依赖于AFNetworking的应用程序,可以将数据从私有API加载到Mantle模型中。我的API客户端是AFHTTPSessionManager
的单例子类。
我正试图通过利用NSURLCache
来实现基本的离线模式。如果dataTaskWithRequest:request:completionHandler
表示无法访问网络,则方法是将NSURLRequestReturnCacheDataDontLoad
上的请求缓存策略修改为AFNetworkReachabilityManager
。
虽然我可以通过在测试期间使用断点来验证这实际上是有效的,但是请求本身并未被缓存。我通过从Xcode下载应用程序容器并检查Cache.db sqlite文件来验证这一点,该文件是空的。我还通过手动创建虚拟NSCachedURLResponse
并使用storeCachedResponse:forRequest
强制将其存储在缓存中来验证我正在查看正确的缓存。然后虚拟响应确实显示在Cache.db sqlite文件中。
所以,我认为我将问题缩小到我的“通常”响应,而不是缓存。我还可以修改服务器API发送的标头。我打算在这里做的是在API响应上设置标头Cache-Control: private
(以便不使其他客户端使用它不应该使用此API缓存响应),并在{}块中修改此缓存标头{1}},但这个区块永远不会发射。
据我所知,URL系统可能会根据一些未记录的规则,主要是setDataTaskWillCacheResponseBlock
标头的存在以及响应大小/缓存大小来决定何时调用URLSession:dataTask:willCacheResponse:completionHandler
来调用该块比。但我的响应是一个145字节的JSON,Cache-Control
标头已设置(我通过Cache-Control
验证并检查我的请求的curl -v
块的task
参数。
我还尝试了一个更积极的缓存标头success
,以查看是否缓存了响应或至少调用了块,但行为完全相同。我还按预期检查了Cache-Control: public, max-age=2592000
属性,该属性确实指向myAFHTTPSessionManagerSubclass.session.delegate
。
我也尝试直接在我的子类中覆盖myAFHTTPSessionManagerSubclass
,但它仍未被调用。在URLSession:dataTask:willCacheResponse:completionHandler
目录中AFURLSessionManager.m
上的同一委托方法上设置断点也不起作用,执行永远不会在断点处停止。
根据我的Pods
文件,我正在使用AFNetworking的2.5.4版。
那么,如何让我的回复缓存(最好不要在响应上设置激进的缓存策略),这样我就可以在我的应用中实现快速离线模式?
编辑:我还尝试创建Podfile.lock
以查看是否可行。所以,我创建了一个简单的Node.js服务器,它只是回答一些简单的事情并设置一个缓存头:
NSURLSession
然后创建了一个简单的视图控制器来测试:
$ curl -v http://192.168.1.107:1337/
* Hostname was NOT found in DNS cache
* Trying 192.168.1.107...
* Connected to 192.168.1.107 (192.168.1.107) port 1337 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.37.1
> Host: 192.168.1.107:1337
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/plain
< Cache-Control: public
< Date: Thu, 11 Jun 2015 14:38:14 GMT
< Connection: keep-alive
< Transfer-Encoding: chunked
<
Hello World
正确打印了响应,但永远不会调用- (void)viewDidLoad {
[super viewDidLoad];
// Prime the cache
[NSURLCache setSharedURLCache:[[NSURLCache alloc] initWithMemoryCapacity:2*1024 diskCapacity:10*1024*1024 diskPath:@"mytestcache"]];
// The sleep is to be absolutely sure the cache did initialise
sleep(2);
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
[[session dataTaskWithURL:[NSURL URLWithString:@"http://192.168.1.107:1337"] completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"Got response: %@", [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
}] resume];
}
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask willCacheResponse:(NSCachedURLResponse *)proposedResponse completionHandler:(void (^)(NSCachedURLResponse *))completionHandler {
completionHandler(proposedResponse);
}
(我设置了一个断点)。我再次尝试检查容器,并创建了willCacheResponse
目录,但它是空的。我也尝试了mytestcache
,得到了相同的结果。
(在https://github.com/AFNetworking/AFNetworking/issues/2780交叉发布到AFNetworking问题)
答案 0 :(得分:2)
好吧,我会让你大吃一惊 - 你上面写的代码工作得很好,但是对于你想要实现的目标它是不正确的;)
原因很简单 - 当您通过方法dataTaskWithRequest:completionHandler:
创建请求时,不会调用委托方法。您需要使用方法dataTaskWithRequest:
。
第二件事是,当我们讨论缓存时,NSURLSession真的是sux。您可能在iOS 7.x / 8.x中遇到了几个问题。当我在我的应用程序中实现自定义缓存时,我遇到了这样的噩梦,最终与NSURLCache没有任何共同之处。我还决定基于NSURLSession和AFNetworking创建我的框架。
iOS 7上的一个问题是使用NSURLSession
创建defaultConfiguration
,NSURLCache
对象与全局对象不同 - 非常意外的行为。
iOS 8的一个问题是POST请求也被缓存,这可能会危及应用程序的安全性。 BTW https://www.google.pl/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=nsurlcache+ios+8+broken
也许它没有回答你的问题,但你应该知道iOS版本之间的奇怪行为和差异。 老实说,我建议您使用AFHTTPRequestOperationManager而不是AFHTTPSessionManager,因为至少NSURLConnection的工作方式与NSURLCache有害:)