如何使用NSURLCache缓存NSURLProtocol提供的内容

时间:2014-04-29 20:30:47

标签: ios objective-c nsurlcache nsurlprotocol

我已经编写了NSURLProtocol来检查针对本地路径映射的http URL的出站plist请求,并提供本地内容,然后将其缓存使用NSURLCache:

- (void)startLoading
{   
    //Could this be why my responses never come out of the cache?
    NSURLResponse *response =[[NSURLResponse alloc]initWithURL:self.request.URL
                                                      MIMEType:nil expectedContentLength:-1
                                              textEncodingName:nil];

    //Get the locally stored data for this request
    NSData* data = [[ELALocalPathSubstitutionService singleton] getLocallyStoredDataForRequest:self.request];

    //Tell the connection to cache the response
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];

    //Have the connection load the data we just fetched
    [[self client] URLProtocol:self didLoadData:data];

    //Tell the connection to finish up
    [[self client] URLProtocolDidFinishLoading:self];
}

我将本地数据提取的次数限制为一次。第一次获取它的意图来自NSBundle,但此后它会使用股票NSURLCache检查它是否应来自缓存网络

+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
    //Check if we have pre-loaded data for that request
    ELAPathSubstitution* pathSub = [[ELALocalPathSubstitutionService singleton] pathSubForRequest:request];

    //We don't have a mapping for this URL
    if (!pathSub)
        return NO;

    //If it's been fetched too many times, don't handle it
    if ([pathSub.timesLocalDataFetched intValue] > 0)
    {
        //Record that we refused it.
        [pathSub addHistoryItem:ELAPathSubstitutionHistoryRefusedByProtocol];
        return NO;
    }

    //Record that we handled it.
    [pathSub addHistoryItem:ELAPathSubstitutionHistoryHandledByProtocol];
    return YES;
}

可悲的是,似乎本地数据会进入缓存,但它不会再回来了。这是一个日志片段:

History of [https://example.com/image.png]:
[2014-04-29 18:01:53 +0000] = [ELAPathSubstitutionHistoryHandledByProtocol]
[2014-04-29 18:01:53 +0000] = [ELAPathSubstitutionHistoryHandledByProtocol]
[2014-04-29 18:01:53 +0000] = [ELAPathSubstitutionHistoryHandledByProtocol]
[2014-04-29 18:01:53 +0000] = [ELAPathSubstitutionHistoryCacheMiss]
[2014-04-29 18:01:53 +0000] = [ELAPathSubstitutionHistoryDataFetched]
[2014-04-29 18:01:53 +0000] = [ELAPathSubstitutionHistoryAddedToCache]
[2014-04-29 18:02:11 +0000] = [ELAPathSubstitutionHistoryRefusedByProtocol]
[2014-04-29 18:02:11 +0000] = [ELAPathSubstitutionHistoryRefusedByProtocol]

[2014-04-29 18:02:11 +0000] = [ELAPathSubstitutionHistoryCacheMiss] 
[2014-04-29 18:02:11 +0000] = [ELAPathSubstitutionHistoryAddedToCache]
[2014-04-29 18:02:50 +0000] = [ELAPathSubstitutionHistoryRefusedByProtocol]
[2014-04-29 18:02:50 +0000] = [ELAPathSubstitutionHistoryCacheHit]

我的期望是,在第一次被协议拒绝后,它会导致一些缓存命中,但它总是将其视为未命中,从服务器获取内容,以及然后我开始获得缓存命中。

我担心我的NSURLProtocol子类以允许缓存它们的方式构造其响应,但是阻止它们被从缓存中拉出来。有什么想法吗?

提前致谢。 :)

3 个答案:

答案 0 :(得分:5)

与URL加载系统的缓存交互是NSURLProtocolClient对象的责任,该对象充当NSURLProtocol的客户端。如果请求使用NSURLRequestUseProtocolCachePolicy作为缓存策略,则由协议实现决定是否应用正确的协议特定规则来确定是否应缓存响应。

协议实现,在任何适合协议的点上,在其客户端上调用URLProtocol:cachedResponseIsValid:,指示缓存的响应有效。然后客户端应该与URL加载系统的缓存层进行交互。

但是,由于系统为我们提供的客户端是私有且不透明的,因此您可能需要自己动手并与协议中的系统缓存进行交互。如果要采用该路径,可以直接使用NSURLCache。第一步是覆盖协议中的-cachedResponse。如果仔细阅读文档,则默认实现仅从传递给初始化程序的值设置此值。覆盖它以便它访问shared URL cache(或您自己的私有URL缓存):

- (NSCachedURLResponse *) cachedResponse {
    return [[NSURLCache sharedURLCache] cachedResponseForRequest:[self request]];
}

现在,在您通常在客户端上呼叫cachedResponseIsValid:的地方,也会将NSCachedURLResponse存储到NSURLCache。例如,当您有一组完整的字节和响应时:

[[NSURLCache sharedURLCache] storeCachedResponse:cachedResponse forRequest:[self request]];

答案 1 :(得分:0)

感谢信息小组,但是,和斯科特一样,我仍然看到缓存问题,我不清楚具体责任在哪里。

我确定我的自定义NSURLProtocol的响应正在被缓存。我通知协议客户端这样[self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];我之后可以手动查询NSURLCache并返回一个合适的NSCachedURLResponse。

这是在后续请求中,在缓存中有一个我不清楚的请求。我假设对于之前提供etag的请求,NSURLProtocol客户端会在请求中设置if-none-match标头进入我的协议?

我是否真的必须在我的自定义协议中实现http缓存的那一面?

答案 2 :(得分:-1)

我已使用自定义NSUrlCache解决了此问题。我认为它比NSUrlProtocol更容易使用。我已经在appstore中的应用程序中使用了代码。其代码发布于github https://github.com/evermeer/EVURLCache