如何知道 NSURLSessionDataTask 响应是否来自缓存?

How to know if NSURLSessionDataTask response came from cache?

我想确定来自 NSURLSessionDataTask 的响应是来自缓存,还是来自服务器

我正在从

创建我的 NSURLSessionDataTask
request.cachePolicy = NSURLRequestUseProtocolCachePolicy;

想到两个简单的选项:

  • 在发出请求之前调用[[NSURLCache sharedURLCache] cachedResponseForRequest:request],存储缓存的响应,然后在接收完数据后再次调用,比较两次缓存的响应,看它们是否相同。
  • 使用 NSURLRequestReturnCacheDataDontLoad 缓存策略发出初始请求,如果失败,使用更合理的策略发出第二个请求。

第一种方法通常更可取,因为第二种方法将 return 缓存中存在的数据,即使它是陈旧的。但是,在极少数情况下(例如离线模式),这可能就是您想要的,这就是我提到它的原因。

如果您只是出于好奇需要信息,您可以通过 Xcode 运行时网络指标查看您的网络使用情况。将策略更改为不同的设置并观察差异。

如果您想使用缓存,那么在客户端禁用缓存并不是一个好的答案。

如果服务器设置了缓存 headers (etag, cache-control="no-store") 然后 NSURLSession 将重新生效并根据来自的 200 / 304 响应为您提供缓存/新鲜响应服务器。但是,在您的代码中,无论 NSUrlSession 收到 200 还是 304,您将始终看到 statusCode 200。这是限制性的,因为您可能希望在响应未更改时跳过解析、re-creating objects 等。

我所做的解决方法是使用响应中的 etag 值来确定是否发生了某些变化

NSHTTPURLResponse *httpResp = (NSHTTPURLResponse *)response;
NSString *etag = (httpResp && [httpResp isKindOfClass:[NSHTTPURLResponse class]]) ? httpResp.allHeaderFields[@"Etag"] : nil;
BOOL somethingHasChanged = [etag isEqualToString:oldEtag];

为了知道 URLSessionDataTask 响应是来自缓存还是网络,您必须使用自定义 URLSession 并为其提供 URLSessionTaskDelegate,它必须实现以下方法:

func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics)

您会在 metrics 中找到非常有用的信息,尤其是请求的事务指标列表。每个事务指标都有一个 属性 resourceFetchType,可以是 .localCache、.networklLoad、.serverPush 或 .unknown。

这里有更多信息:Apple documentation regarding URLSessionTaskDelegate