10

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

4

3 回答 3

11

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

一些链接:

于 2013-11-07T22:16:39.590 回答
11

如果有人感兴趣,这里是用 Swift 重写的 Stephanus 答案:

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)

    }

}
于 2014-10-17T15:18:28.410 回答
1

另一种可能的解决方案是修改响应对象并从服务器中删除 Cache-Control 标头并用您自己想要的值替换它们。

有两个地方可以做到这一点。

您可以在 inNSURLSessionDataDelegate中执行此操作func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, willCacheResponse proposedResponse: NSCachedURLResponse, completionHandler: (NSCachedURLResponse?) -> Void),但如果您在此处执行此操作,则无法再使用通常的基于完成处理程序的方法从会话任务中获取结果。

你可以做的另一个地方是定义一个 custom NSURLProtocol,它拦截 HTTP 和 HTTPS 响应并修改它们。

于 2015-09-22T21:28:48.033 回答