【问题标题】:How to set NSURLRequest cache expiration?如何设置 NSURLRequest 缓存过期?
【发布时间】:2013-11-20 05:45:36
【问题描述】:

我正在使用 AFNetworking,需要在一个响应中缓存数据几分钟。所以我在应用委托中设置了 NSUrlCache,然后在我的请求中设置它:

NSMutableURLRequest *request = //obtain request; 
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

然后如何设置过期日期:如果数据是在 n 分钟前加载的,则询问服务器而不是磁盘的响应?

编辑:

假设服务器不支持缓存,我需要在代码中管理它。

【问题讨论】:

    标签: ios objective-c caching


    【解决方案1】:

    所以,我找到了解决方案。

    这个想法是使用connection:willCacheResponse: 方法。在缓存响应之前,它将被执行,在那里我们可以更改响应并返回新的,或者返回 nil 并且不会缓存响应。当我使用 AFNetworking 时,有一个很好的操作方法:

    - (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block;
    

    添加代码:

      [operation setCacheResponseBlock:^NSCachedURLResponse *(NSURLConnection *connection, NSCachedURLResponse *cachedResponse) {
        if([connection currentRequest].cachePolicy == NSURLRequestUseProtocolCachePolicy) {
          cachedResponse = [cachedResponse responseWithExpirationDuration:60];
        }
        return cachedResponse;
      }];
    

    responseWithExpirationDuration 来自类别:

    @interface NSCachedURLResponse (Expiration)
    -(NSCachedURLResponse*)responseWithExpirationDuration:(int)duration;
    @end
    
    @implementation NSCachedURLResponse (Expiration)
    
    -(NSCachedURLResponse*)responseWithExpirationDuration:(int)duration {
      NSCachedURLResponse* cachedResponse = self;
      NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)[cachedResponse response];
      NSDictionary *headers = [httpResponse allHeaderFields];
      NSMutableDictionary* newHeaders = [headers mutableCopy];
    
      newHeaders[@"Cache-Control"] = [NSString stringWithFormat:@"max-age=%i", duration];
      [newHeaders removeObjectForKey:@"Expires"];
      [newHeaders removeObjectForKey:@"s-maxage"];
    
      NSHTTPURLResponse* newResponse = [[NSHTTPURLResponse alloc] initWithURL:httpResponse.URL
                                                                   statusCode:httpResponse.statusCode
                                                                  HTTPVersion:@"HTTP/1.1"
                                                                 headerFields:newHeaders];
    
      cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:newResponse
                                                                data:[cachedResponse.data mutableCopy]
                                                            userInfo:newHeaders
                                                       storagePolicy:cachedResponse.storagePolicy];
      return cachedResponse;
    }
    
    @end
    

    所以,我们根据http/1.1在http头中设置过期秒数 为此,我们需要设置一个标题: 过期,缓存控制:s-maxage 或 max-age 然后创建新的缓存响应,因为属性是只读的,并返回新对象。

    【讨论】:

    • 当服务器返回304(未修改)时,这将在后续请求中不起作用。请注意,在 AFNetworking 中仅在 200 返回码而不是 304 时调用 setCacheResponseBlock。因此对于后续请求,如果服务器确定资源没有更改(304),您仍然会最终发出后续请求,直到服务器下次返回 200 .
    • 如果我使用UIImageView (_AFNetworking)setImageWithURLRequest,我如何调用setCacheResponseBlock,因为它不公开af_imageRequestOperation,并且没有设置缓存块的功能?
    • @huync 我不使用 AFNetworking 进行图像加载,而是使用 SDWebImage 库,它有很好的内置图像缓存系统
    【解决方案2】:

    使用 URLSession 的 @HotJard 解决方案的 Swift 等效项

    extension CachedURLResponse {
        func response(withExpirationDuration duration: Int) -> CachedURLResponse {
            var cachedResponse = self
            if let httpResponse = cachedResponse.response as? HTTPURLResponse, var headers = httpResponse.allHeaderFields as? [String : String], let url = httpResponse.url{
    
                headers["Cache-Control"] = "max-age=\(duration)"
                headers.removeValue(forKey: "Expires")
                headers.removeValue(forKey: "s-maxage")
    
                if let newResponse = HTTPURLResponse(url: url, statusCode: httpResponse.statusCode, httpVersion: "HTTP/1.1", headerFields: headers) {
                cachedResponse = CachedURLResponse(response: newResponse, data: cachedResponse.data, userInfo: headers, storagePolicy: cachedResponse.storagePolicy)
                }
            }
            return cachedResponse
        }
    }
    

    然后在你的自定义类中实现 URLSessionDataDelegate 协议

    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) {
    
        if dataTask.currentRequest?.cachePolicy == .useProtocolCachePolicy {
            let newResponse = proposedResponse.response(withExpirationDuration: 60)
            completionHandler(newResponse)
        }else {
            completionHandler(proposedResponse)
        }
    }
    

    不要忘记创建配置和会话,将自定义类作为委托引用传递,例如

    let session = URLSession(
            configuration: URLSession.shared.configuration,
            delegate: *delegateReference*,
            delegateQueue: URLSession.shared.delegateQueue
        )
    let task = session.dataTask(with: request)
    task.resume()
    

    【讨论】:

    • 太棒了!是否可以将其与 AlamoFire 一起使用?
    • 我还没有真正将 AlamoFire 用于生产应用程序,所以我不太确定。但我认为应该适用相同的原则。
    • 从 AlamoFire 的文档中,您可以覆盖闭包 - 打开 var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?在 AlamoFire 的 SessionManager 的 SessionDelegate 上,它映射到 URLSession 的 Overrides urlSession(_:dataTask:willCacheResponse:completionHandler:) 并实现与上面相同的逻辑。见github.com/Alamofire/Alamofire/blob/master/Documentation/…
    • 我已经这样做了。 Cache-Control 标头已正确添加到 alamofire 委托中的缓存响应中,但除了在请求中使用 .useProtocolCachePolicy 之外,始终会调用服务。我会继续研究这个
    • 我喜欢这个解决方案,虽然我认为我只是部分理解它。您是否依赖于每次新响应到达时调用“willCacheResponse”?这总是发生吗?有人提到的“304”服务器回复(未修改)呢?然后 - 在这种情况下,我能否以某种方式根据 NSURLRequest 参数决定不缓存某些响应?在我们的例子中,服务器不会缓存响应,而是在响应主体的某处提供响应的“生命周期”,有时 - 它是“一次性使用”的。
    【解决方案3】:

    NSURLCache 中的响应过期由 HTTP 响应中的 Cache-Control 标头控制。

    编辑我看到你更新了你的问题。如果服务器未在响应中提供 Cache-Control 标头,则不会对其进行缓存。对该端点的每个请求都将加载该端点,而不是返回缓存的响应。

    【讨论】:

    • 问题是服务器不支持缓存时如何设置过期
    • 如果服务器没有在响应中提供 Cache-Control 标头,则不会被缓存。对该端点的每个请求都将加载该端点,而不是返回一个缓存的响应。
    • 如果标头包含选项 Cache-Control (max-age or s-maxage) OR Expires,则支持缓存
    猜你喜欢
    • 1970-01-01
    • 2011-10-31
    • 1970-01-01
    • 2012-03-18
    • 2016-01-15
    • 1970-01-01
    • 2011-11-04
    • 2011-12-27
    • 1970-01-01
    相关资源
    最近更新 更多