ios – 绕过http响应头缓存控制:如何设置缓存过期?

来自服务器的所有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秒.

解决方法

您可以实现一个自定义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];
    }
}

一些链接

> Useful info on NSURLCache
> Creating a custom NSURLProtocol

相关文章

UITabBarController 是 iOS 中用于管理和显示选项卡界面的一...
UITableView的重用机制避免了频繁创建和销毁单元格的开销,使...
Objective-C中,类的实例变量(instance variables)和属性(...
从内存管理的角度来看,block可以作为方法的传入参数是因为b...
WKWebView 是 iOS 开发中用于显示网页内容的组件,它是在 iO...
OC中常用的多线程编程技术: 1. NSThread NSThread是Objecti...