具有AFNetworking和离线模式的SDURLCache无法正常工作 [英] SDURLCache with AFNetworking and offline mode not working

查看:132
本文介绍了具有AFNetworking和离线模式的SDURLCache无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 AFNetworking SDURLCache 进行所有网络操作。

I'm using AFNetworking and SDURLCache for all my networking operations.

我有 SDURLCache 设置如下:

SDURLCache *urlCache = [[SDURLCache alloc]
        initWithMemoryCapacity:1024*1024*2   // 2MB mem cache
        diskCapacity:1024*1024*15 // 15MB disk cache
        diskPath:[SDURLCache defaultCachePath]];
    [urlCache setMinCacheInterval:1];
    [NSURLCache setSharedURLCache:urlCache];

我的所有请求都使用cachePolicy NSURLRequestUseProtocolCachePolicy ,根据apple docs的工作原理如下:

All my request are using cachePolicy NSURLRequestUseProtocolCachePolicy, which according to apple docs works like this:


如果请求中不存在NSCachedURLResponse,则获取
数据来自原始来源。如果请求有缓存的
响应,则URL加载系统会检查响应
以确定它是否指定必须重新验证内容。如果
必须重新验证内容,则会与
原始来源建立连接以查看其是否已更改。如果它没有改变,
则从本地缓存返回响应。如果它已更改,
将从原始源获取数据。

If an NSCachedURLResponse does not exist for the request, then the data is fetched from the originating source. If there is a cached response for the request, the URL loading system checks the response to determine if it specifies that the contents must be revalidated. If the contents must be revalidated a connection is made to the originating source to see if it has changed. If it has not changed, then the response is returned from the local cache. If it has changed, the data is fetched from the originating source.

如果缓存的响应未指定必须重新验证内容,则最大值检查响应
中指定的年龄或到期时间。如果缓存的响应足够新,则从本地缓存返回
响应。如果确定响应是
过时,则检查原始源是否有更新的
数据。如果有更新的数据,则从
发起源获取数据,否则从缓存中返回。

If the cached response doesn’t specify that the contents must be revalidated, the maximum age or expiration specified in the response is examined. If the cached response is recent enough, then the response is returned from the local cache. If the response is determined to be stale, the originating source is checked for newer data. If newer data is available, the data is fetched from the originating source, otherwise it is returned from the cache.

因此,只要缓存不是陈旧的,即使在飞行模式下一切都能正常工作。当缓存过期(max-age和其他)时,会调用失败块。

So everything works perfectly even in airplane mode as long as the cache is not stale. When the cache expires (max-age and others), the failure block gets called.

我一直在 SDURLCache 并且此方法返回包含有效数据的响应(我已将数据解析为字符串并且包含缓存的信息)

I've been digging a little inside the SDURLCache and this method returns a response with valid data (I've parsed the data to a string and it contains the cached information)

- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
    request = [SDURLCache canonicalRequestForRequest:request];

    NSCachedURLResponse *memoryResponse =
        [super cachedResponseForRequest:request];
    if (memoryResponse) {
        return memoryResponse;
    }

    NSString *cacheKey = [SDURLCache cacheKeyForURL:request.URL];

    // NOTE: We don't handle expiration here as even staled cache data is
    // necessary for NSURLConnection to handle cache revalidation.
    // Staled cache data is also needed for cachePolicies which force the
    // use of the cache.
    __block NSCachedURLResponse *response = nil;
    dispatch_sync(get_disk_cache_queue(), ^{
        NSMutableDictionary *accesses = [self.diskCacheInfo
            objectForKey:kAFURLCacheInfoAccessesKey];
        // OPTI: Check for cache-hit in in-memory dictionary before to hit FS
        if ([accesses objectForKey:cacheKey]) {
            response = [NSKeyedUnarchiver unarchiveObjectWithFile:
                [_diskCachePath stringByAppendingPathComponent:cacheKey]];
            if (response) {
                // OPTI: Log entry last access time for LRU cache eviction
                // algorithm but don't save the dictionary
                // on disk now in order to save IO and time
                [accesses setObject:[NSDate date] forKey:cacheKey];
                _diskCacheInfoDirty = YES;
            }
        }
    });

    // OPTI: Store the response to memory cache for potential future requests
    if (response) {
        [super storeCachedResponse:response forRequest:request];
    }

    return response;
}

所以在这一点上我不知道该怎么做,因为我相信响应由操作系统处理,然后 AFNetworking 收到

So at this point I have no idea what to do, because I believe that the response is handled by the OS and then AFNetworking receives a

- (void)connection:(NSURLConnection *)__unused connection 
  didFailWithError:(NSError *)error

里面 AFURLConnectionOperation

推荐答案

好吧我终于达不到了如此丑陋的解决方法:

Well I've finally reached a not so ugly workaround:

第一次

如果你正在使用IOS5 / IOS6您可以删除SDURLCache并使用原生的:

If you're using IOS5/IOS6 you can drop SDURLCache and use the native one:

//Set Cache
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
                                                     diskCapacity:20 * 1024 * 1024
                                                         diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];

但请记住,在IOS5中,https请求不会在IOS6中缓存。

But remember that in IOS5 https requests wont be cached in IOS6 they will.

第二

我们需要将以下框架添加到 Prefix.pch 所以AFNetworking可以开始监控我们的互联网连接。

We need to add the following frameworks to our Prefix.pch so AFNetworking can start monitoring our internet connection.

#import <MobileCoreServices/MobileCoreServices.h>
#import <SystemConfiguration/SystemConfiguration.h>

第三

我们需要和AFHTTPClient实例,所以我们可以拦截每个传出请求并更改他的 cachePolicy

We need and AFHTTPClient instance so we can intercept every outgoing request and change his cachePolicy

-(NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {

    NSMutableURLRequest * request = [super requestWithMethod:method path:path parameters:parameters];
    if (request.cachePolicy == NSURLRequestUseProtocolCachePolicy && self.networkReachabilityStatus == AFNetworkReachabilityStatusNotReachable) {
        request.cachePolicy = NSURLRequestReturnCacheDataDontLoad;
    }

    if (self.networkReachabilityStatus == AFNetworkReachabilityStatusUnknown) {

        puts("uknown reachability status");
    }

    return request;
}

凭借这些代码和平,我们现在可以检测到wifi / 3g何时不可用并指定始终使用缓存的请求,无论如何。 (离线模式)

With these peaces of code we can now detect when the wifi/3g is unavailable and the specify the request to use always the cache no matter what. (Offline Mode)

注释


  • networkReachabilityStatus AFNetworkReachabilityStatusUnknown 时,我仍然不知道该怎么办这可能发生请求一旦应用程序启动并且AF尚未获得互联网状态。

  • I still don't know what to do when the networkReachabilityStatus is AFNetworkReachabilityStatusUnknown This can happen is a request is made as soon as the application starts and AF has not obtained the internet status yet.

请记住,为了使其正常工作,服务器必须设置正确的缓存http响应中的标题。

Remember that in order for this to work the server has to set the correct cache headers in the http response.

更新

看起来IOS6在无互联网情况下加载缓存响应时遇到一些问题,因此即使请求被缓存且请求缓存策略被设置为 NSURLRequestReturnCacheDataDontLoad 请求将失败。

Looks like IOS6 has some problems loading cached responses in no-internet situations, so even if the request is cached and the request cache policy is seted to NSURLRequestReturnCacheDataDontLoad the request will fail.

所以一个丑陋的解决方法是修改(void)连接:(NSURLConnection __unused *)connection
didFailWithError: (NSError *)错误
AFURLConnectionOperation.m 中检索缓存响应,如果请求失败但仅针对特定缓存策略。

So an ugly workaround is to modify (void)connection:(NSURLConnection __unused *)connection didFailWithError:(NSError *)error in AFURLConnectionOperation.m to retrieve the cached response if the request fails but only for specific cache policies.

- (void)connection:(NSURLConnection __unused *)connection
  didFailWithError:(NSError *)error
{
    self.error = error;

    [self.outputStream close];

    [self finish];

    self.connection = nil;

    //Ugly hack for making the request succeed if we can find a valid non-empty cached request
    //This is because IOS6 is not handling cache responses right when we are in a no-connection sittuation
    //Only use this code for cache policies that are supposed to listen to cache regarding it's expiration date
    if (self.request.cachePolicy == NSURLRequestUseProtocolCachePolicy ||
        self.request.cachePolicy == NSURLRequestReturnCacheDataElseLoad ||
        self.request.cachePolicy == NSURLRequestReturnCacheDataDontLoad) {

        NSCachedURLResponse * cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.request];
        if (cachedResponse.data.length > 0) {
            self.responseData = cachedResponse.data;
            self.response = cachedResponse.response;
            self.error = nil;
        }
    }
}

这篇关于具有AFNetworking和离线模式的SDURLCache无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆