AFHTTPSessionManager 上的 setDataTaskWillCacheResponseBlock 设置的块永远不会被调用
Posted
技术标签:
【中文标题】AFHTTPSessionManager 上的 setDataTaskWillCacheResponseBlock 设置的块永远不会被调用【英文标题】:Block set by setDataTaskWillCacheResponseBlock on AFHTTPSessionManager is never called 【发布时间】:2015-06-11 13:01:19 【问题描述】:我开发了一个应用程序,它严重依赖 AFNetworking 将数据从私有 API 加载到 Mantle 模型中。我的 API 客户端是 AFHTTPSessionManager
的单例子类。
我正在尝试通过利用NSURLCache
来实现基本的离线模式。方法是如果AFNetworkReachabilityManager
表示网络不可达,则将dataTaskWithRequest:request:completionHandler
上的请求缓存策略修改为NSURLRequestReturnCacheDataDontLoad
。
虽然我可以通过在测试期间使用断点来验证这确实有效,但请求本身并没有被缓存。我通过从 Xcode 下载应用程序容器并检查 Cache.db sqlite 文件来验证这一点,该文件是空的。我还通过手动创建一个虚拟NSCachedURLResponse
并使用storeCachedResponse:forRequest
将其强制存储在缓存中来验证我正在查看正确的缓存。然后虚拟响应确实出现在 Cache.db sqlite 文件中。
所以,我认为我将问题缩小到我的“通常”响应没有被缓存。我还可以修改服务器 API 发送的标头。我打算在这里做的是在 API 响应上设置一个标头 Cache-Control: private
(以免使其他使用此 API 缓存响应的客户端不应该使用),并在 setDataTaskWillCacheResponseBlock
块中修改此缓存标头,但这个块永远不会触发。
我了解 URL 系统可能会根据一些未记录的规则(主要是 Cache-Control
标头的存在以及响应大小/缓存大小比率)来决定何时调用调用块的 URLSession:dataTask:willCacheResponse:completionHandler
。但是我的响应是一个 145 字节的 JSON,并且设置了 Cache-Control
标头(我通过 curl -v
验证并检查了我的请求的 success
块的 task
参数。
我还尝试了更激进的缓存标头Cache-Control: public, max-age=2592000
,以查看它是否缓存响应或至少调用块,但行为完全相同。我还检查了myAFHTTPSessionManagerSubclass.session.delegate
属性,正如预期的那样,它确实指向myAFHTTPSessionManagerSubclass
。
我也尝试在我的子类中直接覆盖URLSession:dataTask:willCacheResponse:completionHandler
,但它仍然没有被调用。在 Pods
目录中的 AFURLSessionManager.m
上的同一委托方法上设置断点也不起作用,执行永远不会在断点处停止。
根据我的Podfile.lock
文件,我使用的是 AFNetworking 2.5.4 版。
那么,如何让我的响应缓存(最好不要对响应设置积极的缓存策略),以便我可以在我的应用中实现快速离线模式?
编辑:我还尝试创建一个NSURLSession
看看这是否可行。所以,我创建了一个简单的 Node.js 服务器,它只回答一些简单的问题并设置一个缓存头:
$ 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
目录,但它是空的。我也试过Cache-Control: max-age=86400
,得到同样的结果。
(交叉发布到 AFNetworking 问题:https://github.com/AFNetworking/AFNetworking/issues/2780)
【问题讨论】:
【参考方案1】:好吧,我会让你大吃一惊 - 你上面写的代码工作得很好,......但它不适合你想要实现的目标 ;)
原因很简单 - 当您通过方法 dataTaskWithRequest:completionHandler:
创建请求时,没有调用委托方法。您需要改用 dataTaskWithRequest:
方法。
https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/UsingNSURLSession.html#//apple_ref/doc/uid/TP40013509-SW1 - 先读注意
第二件事是,当我们谈论缓存时,NSURLSession 真的很糟糕。在 iOS 7.x/8.x 中您可能会遇到几个问题。当我在我的应用程序中实现自定义缓存时,我遇到了这样的噩梦,它最终与 NSURLCache 没有任何共同之处。此外,我决定创建一种基于 NSURLSession 和 AFNetworking 的框架。
iOS 7 上的一个问题是使用defaultConfiguration
创建NSURLSession
,NSURLCache
对象将与全局对象不同 - 非常出乎意料的行为。
iOS 8 上的问题之一是 POST 请求也被缓存,这可能会危及您的应用程序的安全性。顺便说一句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 是有害的 :)
【讨论】:
以上是关于AFHTTPSessionManager 上的 setDataTaskWillCacheResponseBlock 设置的块永远不会被调用的主要内容,如果未能解决你的问题,请参考以下文章
使用 AFNetworking 2.0 的 POST 请求 - AFHTTPSessionManager
AFNetworking 2.0 AFHTTPSessionManager
使用 AFHTTPSessionManager 时的内存泄漏