NSURLSession 不使用缓存的响应

Posted

技术标签:

【中文标题】NSURLSession 不使用缓存的响应【英文标题】:NSURLSession not using cached responses 【发布时间】:2016-09-06 23:59:55 【问题描述】:

当我直接查询NSURLCache 时,我能够看到缓存的响应,但是当我通过NSURLSession:dataTaskWithRequest 请求相同的资源时,它总是查询服务器并且从不给我缓存的响应,即使在禁用互联网的情况下也是如此。

我像这样在application:didFinishLaunchingWithOptions 中配置NSURLCache

let URLCache = NSURLCache(memoryCapacity: 20 * 1024 * 1024,
                          diskCapacity: 80 * 1024 * 1024, diskPath: nil)
NSURLCache.setSharedURLCache(URLCache)

然后我使用这段代码检查缓存并获取响应:

print("cached response is \(NSURLCache.sharedURLCache().cachedResponseForRequest(request)?.response)")

NSURLSession.sharedSession().dataTaskWithRequest(request)  data, response, error in
  print("nsurlsession response \(response)")
.resume()

我的调试打印结果是:

cached response is Optional(<NSHTTPURLResponse: 0x7f809d0ddfe0>  URL: https://api.test.com/stuff   status code: 200, headers 
"Cache-Control" = "public, s-maxage=600";
Connection = "keep-alive";
"Content-Type" = "application/json";
Date = "Tue, 06 Sep 2016 23:41:24 GMT";
Etag = "\"4800a836fee27c56a3cce1f0f2bddaefa5a7785b\"";
Server = "nginx/1.11.3";
"Transfer-Encoding" = Identity;
"X-RateLimit-Limit" = 60;
"X-RateLimit-Remaining" = 2;  )

nsurlsession response Optional(<NSHTTPURLResponse: 0x7f809d202e50>  URL: https://api.test.com/stuff   status code: 200, headers 
"Cache-Control" = "public, s-maxage=600";
Connection = "keep-alive";
"Content-Type" = "application/json";
Date = "Tue, 06 Sep 2016 23:51:52 GMT";
Etag = "\"4800a836fee27c56a3cce1f0f2bddaefa5a7785b\"";
Server = "nginx/1.11.3";
"Transfer-Encoding" = Identity;
"X-RateLimit-Limit" = 60;
"X-RateLimit-Remaining" = 52;  )

如您所见,来自服务器的 Cache-Control 标头设置为允许缓存。我没有对我的请求做任何特别的事情,只是创建一个带有 URL 的默认 NSURLRequest。每次我触发请求时,新的响应都会被缓存,但不会在后续请求中检索到。

NSURLSession 是否有任何理由不使用存储在NSURLCache 中的响应?我必须做些什么来告诉NSURLSession 在缓存中实际查找请求吗?

【问题讨论】:

【参考方案1】:

我不能绝对肯定地告诉你为什么没有查询缓存,但我可以列出最可能的原因:

当询问该 ETag 标头的有效性(IIRC 使用 HEAD 请求)时,服务器没有响应 304。 请求太大 - 无论是相对于缓冲区的大小还是绝对值。缓存应该至少比您通常缓存的请求大几个数量级;超过 5% 的缓存大小将不会被缓存。 请求方法不是 GET。 (只有 GET 请求会被缓存,除非你对机器很感兴趣。) 已经过了 10 多分钟(600 秒不算长)。 请求是在具有不同后备缓存的不同 URL 会话中发出的。 请求是在临时 URL 会话或由于某些其他原因没有缓存的会话中发出的。 会话实际上正在返回缓存的响应,但您看到的是一个请求,因为它重新验证的速度比您预期的要积极一些——可能是因为它很快就会达到其最大年龄。 您的 URL 请求正在由不尊重缓存的自定义 NSURLProtocol 在后台处理(例如,由于某些行为不佳的第三方网络或广告框架)。 当您尝试检索请求时,该请求实际上并未完全写入缓存(由多个线程引起的计时竞争)。

我可能忘记了其他几个。话虽如此,如果我忘记了它们,那可能意味着它们没有记录在案。

所以...

如果您确认上面列出的所有内容都按预期工作,请在 bugreporter.apple.com 上提交错误并包含足够的代码来重现问题,并尽可能提供数据包转储。

【讨论】:

谢谢,这非常很有帮助。作为一名前端工程师,我很难找出所有可能发生网络问题的地方。我尝试点击 TVDB url 来消除客户端配置的问题,并且缓存适用于此,所以我怀疑问题出在 ETag 重新验证上。 原来服务器在 ETag 验证期间没有发送 304。用查尔斯代理验证并修复它。我没有意识到客户端会在响应变得陈旧之前尝试验证。 整洁。很高兴有帮助。 是否可以调试(例如打印)以查看缓存数据是否用于某个数据任务(无需使用 Charles/Wireshark 等工具)?

以上是关于NSURLSession 不使用缓存的响应的主要内容,如果未能解决你的问题,请参考以下文章

可以使用不同的配置多次创建 NSURLSession 对象吗?

如何使用 iOS 7 的 NSURLSession 接受自签名 SSL 证书

NSURLSession学习笔记简介

NSURLCache 用于 NSURLSession 后台任务

NSURLSession 从 Servlet 获取响应

NSURLSession 响应字符串完成块 - Swift