来自服务器的所有http响应都带有标题,告知我们的应用不要缓存响应:
Cache-Control: no-cache
Pragma: no-cache
Expires: 0
因此,如果您使用默认缓存策略“NSURLRequestUseProtocolCachePolicy”创建NSUrlRequests,则应用程序将始终从服务器加载数据。但是,我们需要缓存响应,显而易见的解决方案是将这些标头设置为某个时间(例如在后端),设置为10秒。但我对如何绕过此策略并将每个请求缓存10秒的解决方案感兴趣。
为此您需要设置共享缓存。这可以在AppDelegate didFinishLaunchingWithOptions:
中完成NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
diskCapacity:20 * 1024 * 1024
diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
然后,我们需要嵌入代码来强制缓存响应。如果您使用AFHttpClient的实例,则可以通过覆盖以下方法并手动将缓存存储到共享缓存中来完成:
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
willCacheResponse:(NSCachedURLResponse *)cachedResponse {
NSMutableDictionary *mutableUserInfo = [[cachedResponse userInfo] mutableCopy];
NSMutableData *mutableData = [[cachedResponse data] mutableCopy];
NSURLCacheStoragePolicy storagePolicy = NSURLCacheStorageAllowedInMemoryOnly;
// ...
return [[NSCachedURLResponse alloc] initWithResponse:[cachedResponse response]
data:mutableData
userInfo:mutableUserInfo
storagePolicy:storagePolicy];
}
最后一件事是为请求设置cachePolicy。在我们的例子中,我们希望为所有请求设置相同的缓存策略。所以再次,如果您使用AFHttpClient的实例,那么可以通过覆盖以下方法来完成:
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {
NSMutableURLRequest *request = [super requestWithMethod:method path:path parameters:parameters];
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
return request;
}
到目前为止一切顺利。 “NSURLRequestReturnCacheDataElseLoad”使得第一次执行请求并在其他时间从缓存加载响应。问题是,目前还不清楚如何设置缓存过期时间,例如10秒。
答案 0 :(得分:10)
您可以实现仅返回尚未过期的缓存响应的自定义NSURLCache。
示例:
#import "CustomURLCache.h"
NSString * const EXPIRES_KEY = @"cache date";
int const CACHE_EXPIRES = -10;
@implementation CustomURLCache
// static method for activating this custom cache
+(void)activate {
CustomURLCache *urlCache = [[CustomURLCache alloc] initWithMemoryCapacity:(2*1024*1024) diskCapacity:(2*1024*1024) diskPath:nil] ;
[NSURLCache setSharedURLCache:urlCache];
}
-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
NSCachedURLResponse * cachedResponse = [super cachedResponseForRequest:request];
if (cachedResponse) {
NSDate* cacheDate = [[cachedResponse userInfo] objectForKey:EXPIRES_KEY];
if ([cacheDate timeIntervalSinceNow] < CACHE_EXPIRES) {
[self removeCachedResponseForRequest:request];
cachedResponse = nil;
}
}
return cachedResponse;
}
- (void)storeCachedResponse:(NSCachedURLResponse *)cachedResponse forRequest:(NSURLRequest *)request {
NSMutableDictionary *userInfo = cachedResponse.userInfo ? [cachedResponse.userInfo mutableCopy] : [NSMutableDictionary dictionary];
[userInfo setObject:[NSDate date] forKey:EXPIRES_KEY];
NSCachedURLResponse *newCachedResponse = [[NSCachedURLResponse alloc] initWithResponse:cachedResponse.response data:cachedResponse.data userInfo:userInfo storagePolicy:cachedResponse.storagePolicy];
[super storeCachedResponse:newCachedResponse forRequest:request];
}
@end
如果这不能给你足够的控制权,那么我将使用如下的 startLoading 方法实现自定义NSURLProtocol,并将其与自定义缓存结合使用。
- (void)startLoading
{
NSMutableURLRequest *newRequest = [self.request mutableCopy];
[NSURLProtocol setProperty:@YES forKey:@"CacheSet" inRequest:newRequest];
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];
if (cachedResponse) {
[self connection:nil didReceiveResponse:[cachedResponse response]];
[self connection:nil didReceiveData:[cachedResponse data]];
[self connectionDidFinishLoading:nil];
} else {
_connection = [NSURLConnection connectionWithRequest:newRequest delegate:self];
}
}
一些链接:
答案 1 :(得分:7)
如果有人会感兴趣,这里是Stephanus在Swift中重写的答案:
class CustomURLCache: NSURLCache {
// UserInfo expires key
let kUrlCacheExpiresKey = "CacheData";
// How long is cache data valid in seconds
let kCacheExpireInterval:NSTimeInterval = 60*60*24*5;
// get cache response for a request
override func cachedResponseForRequest(request:NSURLRequest) -> NSCachedURLResponse? {
// create empty response
var response:NSCachedURLResponse? = nil
// try to get cache response
if let cachedResponse = super.cachedResponseForRequest(request) {
// try to get userInfo
if let userInfo = cachedResponse.userInfo {
// get cache date
if let cacheDate = userInfo[kUrlCacheExpiresKey] as NSDate? {
// check if the cache data are expired
if (cacheDate.timeIntervalSinceNow < -kCacheExpireInterval) {
// remove old cache request
self.removeCachedResponseForRequest(request);
} else {
// the cache request is still valid
response = cachedResponse
}
}
}
}
return response;
}
// store cached response
override func storeCachedResponse(cachedResponse: NSCachedURLResponse, forRequest: NSURLRequest) {
// create userInfo dictionary
var userInfo = NSMutableDictionary()
if let cachedUserInfo = cachedResponse.userInfo {
userInfo = NSMutableDictionary(dictionary:cachedUserInfo)
}
// add current date to the UserInfo
userInfo[kUrlCacheExpiresKey] = NSDate()
// create new cached response
let newCachedResponse = NSCachedURLResponse(response:cachedResponse.response, data:cachedResponse.data, userInfo:userInfo,storagePolicy:cachedResponse.storagePolicy)
super.storeCachedResponse(newCachedResponse, forRequest:forRequest)
}
}
答案 2 :(得分:1)
另一种可能的解决方案是修改响应对象并从服务器中删除Cache-Control标头,并用您自己想要的值替换它们。
你可以做两件事。
您可以在NSURLSessionDataDelegate
中的func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, willCacheResponse proposedResponse: NSCachedURLResponse, completionHandler: (NSCachedURLResponse?) -> Void)
中执行此操作,但如果您在那里执行此操作,则无法再使用通常的基于完成处理程序的方法从会话任务中获取结果。
你可以做的另一个地方是定义一个自定义NSURLProtocol
,它拦截HTTP和HTTPS响应并修改它们。