使用 storeCachedResponse 存储在缓存中后未检索到 URLresponse
URLresponse is not retrieved after storing in cache using storeCachedResponse
目标
我正在尝试将 data/response 从 URL 请求注入到 另一个 URL 我缓存中的请求。
设置
这只是一个示例代码。它已准备好转储到项目中。
我想做的是使用从我的 landscapeURLString
网络请求中检索到的响应 + 数据...存储到我的 session 的缓存中 我的蜥蜴URL字符串 请求。
import UIKit
class ViewController: UIViewController {
lazy var defaultSession : URLSession = {
let urlCache = URLCache(memoryCapacity: 500 * 1024 * 1024, diskCapacity: 500 * 1024 * 1024, diskPath: "something")
let configuration = URLSessionConfiguration.default
configuration.urlCache = urlCache
let session = URLSession(configuration: configuration)
return session
}()
lazy var downloadLizzardbutton : UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("download lizard image OFFLINE", for: .normal)
btn.backgroundColor = .blue
btn.addTarget(self, action: #selector(downloadLizardAction), for: .touchUpInside)
return btn
}()
let imageView : UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
return imageView
}()
// I make sure my internet is set to OFF so that it forces this to be read from cache...
@objc func downloadLizardAction() {
downloadImage(from: lizardURLString, from: defaultSession)
}
let lizardURLString = "https://upload.wikimedia.org/wikipedia/commons/e/e0/Large_Scaled_Forest_Lizard.jpg"
let landscapeURLString = "https://images.pexels.com/photos/414171/pexels-photo-414171.jpeg"
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(imageView)
view.addSubview(downloadLizzardbutton)
imageView.pinToAllEdges(of: view)
downloadImage(from: landscapeURLString, from: defaultSession)
}
private func downloadImage(from urlString: String, from session : URLSession){
guard let url = URL(string: urlString) else{
fatalError("bad String we got!")
}
let urlRequest = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 15)
print("url.hashValue: \(urlRequest.hashValue)")
let task = session.dataTask(with: urlRequest) { [weak self] (data, response, error) in
guard error == nil else {
print(error)
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("response NOT 2xx: \(response)")
return
}
for header in httpResponse.allHeaderFields{
if let key = header.key as? String, key == "Cache-Control"{
print("found Cache-Control: \(httpResponse.allHeaderFields["Cache-Control"])")
}
}
if let data = data,
let image = UIImage(data: data){
let lizardURL = URL(string: self!.lizardURLString)
let lizardURLRequest = URLRequest(url: lizardURL!)
let landscapeCachedURLPResponse : CachedURLResponse = CachedURLResponse(response: response!, data: data, userInfo:nil, storagePolicy: .allowed)
print("before storing into cache: \(String(describing: session.configuration.urlCache?.cachedResponse(for: lizardURLRequest)))")
session.configuration.urlCache?.storeCachedResponse(landscapeCachedURLPResponse, for: lizardURLRequest)
print("after storing into cache: \(String(describing: session.configuration.urlCache?.cachedResponse(for: lizardURLRequest)))")
print("lizardRequest.hashValue: \(lizardURLRequest.hashValue)")
DispatchQueue.main.async {
self?.imageView.image = image
}
}
}
task.resume()
}
}
extension UIView{
func pinToAllEdges(of view: UIView){
let leading = leadingAnchor.constraint(equalTo: view.leadingAnchor)
let top = topAnchor.constraint(equalTo: view.topAnchor)
let trailing = trailingAnchor.constraint(equalTo: view.trailingAnchor)
let bottom = bottomAnchor.constraint(equalTo: view.bottomAnchor)
NSLayoutConstraint.activate([leading, top, trailing, bottom])
}
}
我已经验证的事情:
- 我的
landscapeURLString
有 cache-control
header 和 max-age
的 31536000
- 如果是全新安装,那么before存入缓存,我的
cachedResponse
for lizardURL 字符串是 nil
。但是存储后,就不再是nil
了。结果我得出结论,我成功地将一些东西存储到缓存中!
- 我还怀疑 URLCache 将 URLRequest 视为键。所以我打印了
lizardURLString
的 hashValue。它与我存储的密钥相同。结合以上几点,我得出结论,确切的密钥存在于缓存中!
- 我还可以看到,当我将其存储在我的缓存中时,我的
currentMemoryUsage
增加了。
我如何测试以及我看到了什么:
- 我只是下载风景图片。
- 关掉我的网络
- 点击按钮下载蜥蜴图片。
显然它处于离线状态。我希望它从缓存中使用,但事实并非如此。我得到的只是暂停!
我也尝试将 cachePolicy
更改为 returnCacheDataElseLoad
,但这也没有帮助
编辑1:
我也试过按照大卫说的做:
let landscapeHTTPResponse : HTTPURLResponse = HTTPURLResponse(url: self!.lizardURL, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: (httpResponse.allHeaderFields as! [String : String]))!
let landscapedCachedURLPResponse : CachedURLResponse = CachedURLResponse(response: landscapeHTTPResponse, data: data, userInfo:nil, storagePolicy: .allowed)
并将存储的landscapedCachedURLPResponse
存入缓存。那也没用。它也会超时——它不会每次都查看缓存。
编辑2:
所以我取得了一些进步。或者退一步,进一步。
我尝试查看是否可以存储 相同 URL 的响应,并查看是否可以在清空缓存后检索响应。我做不到。
我是这样创建缓存响应的:
let cachedResponse = CachedURLResponse(response: response!, data: data, userInfo:nil, storagePolicy: .allowed)
或者像这样:
let cachedResponse = CachedURLResponse(response: response!, data: data)
是什么让这部分起作用?:
let cachedResponseFromCache = session.configuration.urlCache?.cachedResponse(for: self!.landscapeURLRequest)
self._cachedResponse = cachedResponseFromCache
那我:
- 刷新缓存
- 关闭互联网
- 尝试下载图像,但没有成功,这很好。这是预期的行为
- 已将
cachedResponseFromCache
属性 存储到缓存中。
- 能够从缓存中检索!
我不确定从缓存本身中提取和从 Response + Data
创建缓存之间有什么区别。
这很重要,因为我开始怀疑是否还有某种形式的 internal bugs in URLCache。这让我有理由相信它 可能 会按预期工作。
现在我知道存储到缓存中的过程了。我知道我的 URL 回复很好。我只需要通过映射 URLRequest
编辑3:
Guy Kogus 建议我的 URL 必须来自同一来源。
所以一旦我下载了他提到的 bearImage,我的 lizardImage 就通过了。瞧!
作为我了解到的非常重要的调试说明:即使你在问题的某些部分(它正在为自己缓存风景图像)上取得成功,改变变量(这里改变初始 URL)总能改变整个检测结果。
他怀疑是因为 header 中的 Server
是共享的,这对查找 cachedResponse 很重要。
我驳斥了这种说法,说我的蜥蜴URL请求是在它在线时发出的,所以没有什么可比的,但它有效!
所以下一个想法是,它可能与 URL 的某些部分有关,比如它的第一部分或其他内容。
然后我去修改蜥蜴URL从:
https://upload.wikimedia.org/wikipedia/commons/e/e0/Large_Scaled_Forest_Lizard.jpg
我在URL中添加了哑字。我还向其中添加了额外的片段。我在最后更改了文件类型。
它还在工作。所以我唯一可以得出的结论是 Headers 中的某些人正在做决策。
我的 landscapeURL 的 header 是:(另一个 URL 的缓存对此不起作用)
Content-Length : 997361
x-cache : HIT, MISS
cf-ray : 472793e93ce39574-IAD
x-served-by : cache-lax8621-LAX, cache-iad2132-IAD
cf-cache-status : HIT
Last-Modified : Sun, 14 Oct 2018 2:10:05 GMT
Accept-Ranges : bytes
Vary : Accept-Encoding
x-content-type-options : nosniff
Content-Type : image/jpeg
expect-ct : max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Set-Cookie : __cfduid=d5f5fd59ce5ff9ac86e42f8c008708ae61541004176; expires=Thu, 31-Oct-19 16:42:56 GMT; path=/; domain=.pexels.com; HttpOnly
Expires : Thu, 31 Oct 2019 16:42:56 GMT
Server : cloudflare
Cache-Control : public, max-age=31536000
Date : Wed, 31 Oct 2018 16:42:56 GMT
我的 BearURL 的 header 是:(另一个 URL 的缓存适用于此)
Date : Wed, 31 Oct 2018 16:46:38 GMT
Content-Length : 215104
x-client-ip : 2001:558:1400:4e:808c:2738:43e:36f5
access-control-expose-headers : Age, Date, Content-Length, Content-Range, X-Content-Duration, X-Cache, X-Varnish
x-cache : cp1076 miss, cp1088 hit/21
Age : 27646
Etag : 00e21950bf432476c91b811bb685b6af
Strict-Transport-Security : max-age=106384710; includeSubDomains; preload
x-analytics : https=1;nocookies=1
Accept-Ranges : bytes
x-object-meta-sha1base36 : 42tq5grg9rq1ydmqd4z5hmmqj6h2309
x-varnish : 48388488, 503119619 458396839
x-cache-status : hit-front
Content-Type : image/jpeg
x-trans-id : tx08ed43bbcc1946269a9a3-005bd97070
Last-Modified : Fri, 04 Oct 2013 23:30:08 GMT
Access-Control-Allow-Origin : *
timing-allow-origin : *
x-timestamp : 1380929407.39127
Via : 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)
重要提示:
对于熊URL,为熊URL和蜥蜴URL或任何其他URL缓存都有效。
对于 landscapeURL,缓存只对 landscapeURL 本身起作用。它不适用于任何 other URL。
所以问题的当前状态是:需要包含什么 headers 才能工作
欢迎来到异步缓存的精彩世界。 NSURLCache 是高度异步的。仅仅因为您已将数据推入其中并不意味着它可用于检索。你必须让主 运行 循环 return 才能可用,甚至可能要等一会儿。 return 存储后无法立即响应的情况并不少见。尝试在五秒左右后调度它。
其次,您的缓存可能有点小,无法存储 multi-megabyte 图像。尝试提高它,看看是否有帮助。
最后,当你说 "turn off your Internet?" 你说你要超时是什么意思。通常,如果您在禁用所有连接的情况下将设备置于飞行模式,则在出现故障并出现指示无连接的错误之前,它不应在那里停留很长时间。如果那没有发生,就会发生一些奇怪的事情,就好像 waitsForConnectivity 正在 session 或其他东西上设置一样。 (您没有在后台发出网络请求,是吗?如果是这样,请尝试将 waitsForConnectivity 显式设置为 NO,这样它们就不会等待连接可用。)
此外,对于这种用法,您可能需要去除 Vary: Accept-Encoding header 或提供一致的用户代理字符串。 header 导致缓存基本上是 per-browser。这可能会导致缓存以意想不到的方式运行,并且可能是您所看到的怪异现象的原因。
请注意,去掉 Vary header 有点麻烦,可能不是解决问题的最正确方法;理想情况下,您应该调整任何您必须调整的传出 header 字段,以便它即使在存在 header 的情况下也能正常工作。但是您必须对其进行研究并弄清楚到底需要哪些字段,因为我不知道。 :-)
这不是一个完整的答案,但应该能为您指明正确的方向。
问题与您的代码无关,我相信它基本上没问题。问题与您从 landscapeURLString
获得的响应有关,因为图像存储在 Cloudflare 中。如果您使用来自同一来源的 2 个图像,(例如尝试使用 this bear from wikipedia 而不是来自 images.pexels.com 的图像)它应该可以工作。
我尝试打印出下载 images.pexels.com 图片的响应和 headers,这是我看到的:
response: <NSHTTPURLResponse: 0x600002bf65c0> { URL: https://upload.wikimedia.org/wikipedia/commons/e/e0/Large_Scaled_Forest_Lizard.jpg } { Status Code: 200, Headers {
"Accept-Ranges" = (
bytes
);
"Cache-Control" = (
"public, max-age=31536000"
);
"Content-Length" = (
997361
);
"Content-Type" = (
"image/jpeg"
);
Date = (
"Wed, 31 Oct 2018 11:38:52 GMT"
);
Expires = (
"Thu, 31 Oct 2019 11:38:52 GMT"
);
"Last-Modified" = (
"Fri, 26 Oct 2018 6:31:56 GMT"
);
Server = (
cloudflare
);
Vary = (
"Accept-Encoding"
);
"cf-cache-status" = (
HIT
);
"cf-ray" = (
"4725d67b0ae461bd-BCN"
);
"expect-ct" = (
"max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\""
);
"x-cache" = (
"HIT, MISS"
);
"x-content-type-options" = (
nosniff
);
"x-served-by" = (
"cache-lax8643-LAX, cache-mad9437-MAD"
);
} }
headers: ["Accept-Ranges": "bytes", "Content-Type": "image/jpeg", "Last-Modified": "Fri, 26 Oct 2018 6:31:56 GMT", "Vary": "Accept-Encoding", "cf-ray": "4725d67b0ae461bd-BCN", "Date": "Wed, 31 Oct 2018 11:38:52 GMT", "Server": "cloudflare", "Expires": "Thu, 31 Oct 2019 11:38:52 GMT", "x-content-type-options": "nosniff", "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", "x-cache": "HIT, MISS", "x-served-by": "cache-lax8643-LAX, cache-mad9437-MAD", "cf-cache-status": "HIT", "Content-Length": "997361", "Cache-Control": "public, max-age=31536000"]
可能有一些东西试图将请求 URL 与导致缓存未命中的响应字段相匹配,但我的知识还不足以知道它是什么。其他人可能会为您抓住它(因此我说这个答案不完整)。
我通过将 dataTask completionHandler 中的第一个守卫替换为:
解决了这个问题
guard error == nil else {
print(error)
if let cr = session.configuration.urlCache?.cachedResponse(for: urlRequest){
let image = UIImage(data: cr.data)
DispatchQueue.main.async {
self?.imageView.image = image
}
}
return
}
如果请求失败,它将获取该请求的缓存响应
目标
我正在尝试将 data/response 从 URL 请求注入到 另一个 URL 我缓存中的请求。
设置
这只是一个示例代码。它已准备好转储到项目中。
我想做的是使用从我的 landscapeURLString
网络请求中检索到的响应 + 数据...存储到我的 session 的缓存中 我的蜥蜴URL字符串 请求。
import UIKit
class ViewController: UIViewController {
lazy var defaultSession : URLSession = {
let urlCache = URLCache(memoryCapacity: 500 * 1024 * 1024, diskCapacity: 500 * 1024 * 1024, diskPath: "something")
let configuration = URLSessionConfiguration.default
configuration.urlCache = urlCache
let session = URLSession(configuration: configuration)
return session
}()
lazy var downloadLizzardbutton : UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.setTitle("download lizard image OFFLINE", for: .normal)
btn.backgroundColor = .blue
btn.addTarget(self, action: #selector(downloadLizardAction), for: .touchUpInside)
return btn
}()
let imageView : UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFill
return imageView
}()
// I make sure my internet is set to OFF so that it forces this to be read from cache...
@objc func downloadLizardAction() {
downloadImage(from: lizardURLString, from: defaultSession)
}
let lizardURLString = "https://upload.wikimedia.org/wikipedia/commons/e/e0/Large_Scaled_Forest_Lizard.jpg"
let landscapeURLString = "https://images.pexels.com/photos/414171/pexels-photo-414171.jpeg"
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(imageView)
view.addSubview(downloadLizzardbutton)
imageView.pinToAllEdges(of: view)
downloadImage(from: landscapeURLString, from: defaultSession)
}
private func downloadImage(from urlString: String, from session : URLSession){
guard let url = URL(string: urlString) else{
fatalError("bad String we got!")
}
let urlRequest = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 15)
print("url.hashValue: \(urlRequest.hashValue)")
let task = session.dataTask(with: urlRequest) { [weak self] (data, response, error) in
guard error == nil else {
print(error)
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
print("response NOT 2xx: \(response)")
return
}
for header in httpResponse.allHeaderFields{
if let key = header.key as? String, key == "Cache-Control"{
print("found Cache-Control: \(httpResponse.allHeaderFields["Cache-Control"])")
}
}
if let data = data,
let image = UIImage(data: data){
let lizardURL = URL(string: self!.lizardURLString)
let lizardURLRequest = URLRequest(url: lizardURL!)
let landscapeCachedURLPResponse : CachedURLResponse = CachedURLResponse(response: response!, data: data, userInfo:nil, storagePolicy: .allowed)
print("before storing into cache: \(String(describing: session.configuration.urlCache?.cachedResponse(for: lizardURLRequest)))")
session.configuration.urlCache?.storeCachedResponse(landscapeCachedURLPResponse, for: lizardURLRequest)
print("after storing into cache: \(String(describing: session.configuration.urlCache?.cachedResponse(for: lizardURLRequest)))")
print("lizardRequest.hashValue: \(lizardURLRequest.hashValue)")
DispatchQueue.main.async {
self?.imageView.image = image
}
}
}
task.resume()
}
}
extension UIView{
func pinToAllEdges(of view: UIView){
let leading = leadingAnchor.constraint(equalTo: view.leadingAnchor)
let top = topAnchor.constraint(equalTo: view.topAnchor)
let trailing = trailingAnchor.constraint(equalTo: view.trailingAnchor)
let bottom = bottomAnchor.constraint(equalTo: view.bottomAnchor)
NSLayoutConstraint.activate([leading, top, trailing, bottom])
}
}
我已经验证的事情:
- 我的
landscapeURLString
有cache-control
header 和max-age
的31536000
- 如果是全新安装,那么before存入缓存,我的
cachedResponse
for lizardURL 字符串是nil
。但是存储后,就不再是nil
了。结果我得出结论,我成功地将一些东西存储到缓存中! - 我还怀疑 URLCache 将 URLRequest 视为键。所以我打印了
lizardURLString
的 hashValue。它与我存储的密钥相同。结合以上几点,我得出结论,确切的密钥存在于缓存中! - 我还可以看到,当我将其存储在我的缓存中时,我的
currentMemoryUsage
增加了。
我如何测试以及我看到了什么:
- 我只是下载风景图片。
- 关掉我的网络
- 点击按钮下载蜥蜴图片。
显然它处于离线状态。我希望它从缓存中使用,但事实并非如此。我得到的只是暂停!
我也尝试将 cachePolicy
更改为 returnCacheDataElseLoad
,但这也没有帮助
编辑1:
我也试过按照大卫说的做:
let landscapeHTTPResponse : HTTPURLResponse = HTTPURLResponse(url: self!.lizardURL, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: (httpResponse.allHeaderFields as! [String : String]))!
let landscapedCachedURLPResponse : CachedURLResponse = CachedURLResponse(response: landscapeHTTPResponse, data: data, userInfo:nil, storagePolicy: .allowed)
并将存储的landscapedCachedURLPResponse
存入缓存。那也没用。它也会超时——它不会每次都查看缓存。
编辑2:
所以我取得了一些进步。或者退一步,进一步。
我尝试查看是否可以存储 相同 URL 的响应,并查看是否可以在清空缓存后检索响应。我做不到。
我是这样创建缓存响应的:
let cachedResponse = CachedURLResponse(response: response!, data: data, userInfo:nil, storagePolicy: .allowed)
或者像这样:
let cachedResponse = CachedURLResponse(response: response!, data: data)
是什么让这部分起作用?:
let cachedResponseFromCache = session.configuration.urlCache?.cachedResponse(for: self!.landscapeURLRequest)
self._cachedResponse = cachedResponseFromCache
那我:
- 刷新缓存
- 关闭互联网
- 尝试下载图像,但没有成功,这很好。这是预期的行为
- 已将
cachedResponseFromCache
属性 存储到缓存中。 - 能够从缓存中检索!
我不确定从缓存本身中提取和从 Response + Data
创建缓存之间有什么区别。
这很重要,因为我开始怀疑是否还有某种形式的 internal bugs in URLCache。这让我有理由相信它 可能 会按预期工作。
现在我知道存储到缓存中的过程了。我知道我的 URL 回复很好。我只需要通过映射 URLRequest
编辑3:
Guy Kogus 建议我的 URL 必须来自同一来源。 所以一旦我下载了他提到的 bearImage,我的 lizardImage 就通过了。瞧!
作为我了解到的非常重要的调试说明:即使你在问题的某些部分(它正在为自己缓存风景图像)上取得成功,改变变量(这里改变初始 URL)总能改变整个检测结果。
他怀疑是因为 header 中的 Server
是共享的,这对查找 cachedResponse 很重要。
我驳斥了这种说法,说我的蜥蜴URL请求是在它在线时发出的,所以没有什么可比的,但它有效! 所以下一个想法是,它可能与 URL 的某些部分有关,比如它的第一部分或其他内容。
然后我去修改蜥蜴URL从:
https://upload.wikimedia.org/wikipedia/commons/e/e0/Large_Scaled_Forest_Lizard.jpg
我在URL中添加了哑字。我还向其中添加了额外的片段。我在最后更改了文件类型。
它还在工作。所以我唯一可以得出的结论是 Headers 中的某些人正在做决策。
我的 landscapeURL 的 header 是:(另一个 URL 的缓存对此不起作用)
Content-Length : 997361
x-cache : HIT, MISS
cf-ray : 472793e93ce39574-IAD
x-served-by : cache-lax8621-LAX, cache-iad2132-IAD
cf-cache-status : HIT
Last-Modified : Sun, 14 Oct 2018 2:10:05 GMT
Accept-Ranges : bytes
Vary : Accept-Encoding
x-content-type-options : nosniff
Content-Type : image/jpeg
expect-ct : max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Set-Cookie : __cfduid=d5f5fd59ce5ff9ac86e42f8c008708ae61541004176; expires=Thu, 31-Oct-19 16:42:56 GMT; path=/; domain=.pexels.com; HttpOnly
Expires : Thu, 31 Oct 2019 16:42:56 GMT
Server : cloudflare
Cache-Control : public, max-age=31536000
Date : Wed, 31 Oct 2018 16:42:56 GMT
我的 BearURL 的 header 是:(另一个 URL 的缓存适用于此)
Date : Wed, 31 Oct 2018 16:46:38 GMT
Content-Length : 215104
x-client-ip : 2001:558:1400:4e:808c:2738:43e:36f5
access-control-expose-headers : Age, Date, Content-Length, Content-Range, X-Content-Duration, X-Cache, X-Varnish
x-cache : cp1076 miss, cp1088 hit/21
Age : 27646
Etag : 00e21950bf432476c91b811bb685b6af
Strict-Transport-Security : max-age=106384710; includeSubDomains; preload
x-analytics : https=1;nocookies=1
Accept-Ranges : bytes
x-object-meta-sha1base36 : 42tq5grg9rq1ydmqd4z5hmmqj6h2309
x-varnish : 48388488, 503119619 458396839
x-cache-status : hit-front
Content-Type : image/jpeg
x-trans-id : tx08ed43bbcc1946269a9a3-005bd97070
Last-Modified : Fri, 04 Oct 2013 23:30:08 GMT
Access-Control-Allow-Origin : *
timing-allow-origin : *
x-timestamp : 1380929407.39127
Via : 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)
重要提示:
对于熊URL,为熊URL和蜥蜴URL或任何其他URL缓存都有效。 对于 landscapeURL,缓存只对 landscapeURL 本身起作用。它不适用于任何 other URL。
所以问题的当前状态是:需要包含什么 headers 才能工作
欢迎来到异步缓存的精彩世界。 NSURLCache 是高度异步的。仅仅因为您已将数据推入其中并不意味着它可用于检索。你必须让主 运行 循环 return 才能可用,甚至可能要等一会儿。 return 存储后无法立即响应的情况并不少见。尝试在五秒左右后调度它。
其次,您的缓存可能有点小,无法存储 multi-megabyte 图像。尝试提高它,看看是否有帮助。
最后,当你说 "turn off your Internet?" 你说你要超时是什么意思。通常,如果您在禁用所有连接的情况下将设备置于飞行模式,则在出现故障并出现指示无连接的错误之前,它不应在那里停留很长时间。如果那没有发生,就会发生一些奇怪的事情,就好像 waitsForConnectivity 正在 session 或其他东西上设置一样。 (您没有在后台发出网络请求,是吗?如果是这样,请尝试将 waitsForConnectivity 显式设置为 NO,这样它们就不会等待连接可用。)
此外,对于这种用法,您可能需要去除 Vary: Accept-Encoding header 或提供一致的用户代理字符串。 header 导致缓存基本上是 per-browser。这可能会导致缓存以意想不到的方式运行,并且可能是您所看到的怪异现象的原因。
请注意,去掉 Vary header 有点麻烦,可能不是解决问题的最正确方法;理想情况下,您应该调整任何您必须调整的传出 header 字段,以便它即使在存在 header 的情况下也能正常工作。但是您必须对其进行研究并弄清楚到底需要哪些字段,因为我不知道。 :-)
这不是一个完整的答案,但应该能为您指明正确的方向。
问题与您的代码无关,我相信它基本上没问题。问题与您从 landscapeURLString
获得的响应有关,因为图像存储在 Cloudflare 中。如果您使用来自同一来源的 2 个图像,(例如尝试使用 this bear from wikipedia 而不是来自 images.pexels.com 的图像)它应该可以工作。
我尝试打印出下载 images.pexels.com 图片的响应和 headers,这是我看到的:
response: <NSHTTPURLResponse: 0x600002bf65c0> { URL: https://upload.wikimedia.org/wikipedia/commons/e/e0/Large_Scaled_Forest_Lizard.jpg } { Status Code: 200, Headers {
"Accept-Ranges" = (
bytes
);
"Cache-Control" = (
"public, max-age=31536000"
);
"Content-Length" = (
997361
);
"Content-Type" = (
"image/jpeg"
);
Date = (
"Wed, 31 Oct 2018 11:38:52 GMT"
);
Expires = (
"Thu, 31 Oct 2019 11:38:52 GMT"
);
"Last-Modified" = (
"Fri, 26 Oct 2018 6:31:56 GMT"
);
Server = (
cloudflare
);
Vary = (
"Accept-Encoding"
);
"cf-cache-status" = (
HIT
);
"cf-ray" = (
"4725d67b0ae461bd-BCN"
);
"expect-ct" = (
"max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\""
);
"x-cache" = (
"HIT, MISS"
);
"x-content-type-options" = (
nosniff
);
"x-served-by" = (
"cache-lax8643-LAX, cache-mad9437-MAD"
);
} }
headers: ["Accept-Ranges": "bytes", "Content-Type": "image/jpeg", "Last-Modified": "Fri, 26 Oct 2018 6:31:56 GMT", "Vary": "Accept-Encoding", "cf-ray": "4725d67b0ae461bd-BCN", "Date": "Wed, 31 Oct 2018 11:38:52 GMT", "Server": "cloudflare", "Expires": "Thu, 31 Oct 2019 11:38:52 GMT", "x-content-type-options": "nosniff", "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", "x-cache": "HIT, MISS", "x-served-by": "cache-lax8643-LAX, cache-mad9437-MAD", "cf-cache-status": "HIT", "Content-Length": "997361", "Cache-Control": "public, max-age=31536000"]
可能有一些东西试图将请求 URL 与导致缓存未命中的响应字段相匹配,但我的知识还不足以知道它是什么。其他人可能会为您抓住它(因此我说这个答案不完整)。
我通过将 dataTask completionHandler 中的第一个守卫替换为:
解决了这个问题guard error == nil else {
print(error)
if let cr = session.configuration.urlCache?.cachedResponse(for: urlRequest){
let image = UIImage(data: cr.data)
DispatchQueue.main.async {
self?.imageView.image = image
}
}
return
}
如果请求失败,它将获取该请求的缓存响应