2012-04-20 13 views
36

我爲我的所有網絡操作使用AFNetworkingSDURLCache帶AFNetworking和脫機模式不起作用的SDURLCache

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,根據蘋果的文檔,其工作原理是這樣的:

如果NSCachedURLResponse沒有爲請求存在,那麼 數據從原始源獲取。如果該請求有緩存的 響應,則URL加載系統檢查響應 以確定它是否指定內容必須重新驗證。如果 的內容必須重新驗證,則連接到 原始源,以查看它是否已更改。如果它沒有改變,則 然後從本地緩存返回響應。如果它發生了變化,則從原始源獲取數據 。

如果緩存的響應未指定內容必須重新生效,則會檢查響應 中指定的最大使用期限或到期日期。如果緩存的響應足夠近,則從本地緩存中返回 響應。如果響應是 確定爲陳舊,則檢查始發源以獲取更新的 數據。如果更新的數據可用,則將從 原始源獲取數據,否則將從緩存中返回。

因此,即使在飛行模式下,只要緩存沒有陳舊,一切都可以完美運行。當緩存過期時(max-age和其他),失敗塊被調用。

我已經挖了一點SDURLCache內,此方法返回的有效數據的響應(我已經分析的數據爲字符串,它包含緩存信息)

- (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收到

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

AFURLConnectionOperation

+0

我現在面對完全相同的問題。你找到了解決方案嗎? – mkto 2012-05-20 18:33:43

+0

沒有,我做了一個awefull解決辦法,沒什麼可:-( – Ecarrion 2012-05-21 15:46:01

+0

我已經發出了郵件的作者皮特誰分叉的SDURLCache驕傲希望他有一個答案.. – mkto 2012-05-22 01:46:56

回答

10

嗯,我終於達到了一個不那麼難看的解決方法:

首先

如果你使用的IOS5/iOS6的,你可以刪除SDURLCache並使用本機之一:

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

但請記住,在IOS5 https請求不會緩存在IOS6他們會。

我們需要以下框架添加到我們的Prefix.pch所以AFNetworking可以開始監視我們的互聯網連接。

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

我們需要和AFHTTPClient實例,這樣我們就可以截獲每個傳出的請求,並改變他的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不可用,並指定無論如何都始終使用緩存的請求。 (離線模式)

注意

  • 我還是不知道該怎麼辦時,networkReachabilityStatusAFNetworkReachabilityStatusUnknown發生這種情況是申請儘快作出在應用程序啓動和自動對焦也沒有獲得了互聯網的地位呢。

  • 請記住,爲了這個工作的服務器設置正確的緩存頭在HTTP響應。

UPDATE

看起來iOS6的有一些問題加載在無網絡的情況下緩存的響應,所以即使請求被高速緩存,並請求緩存策略seted到NSURLRequestReturnCacheDataDontLoad請求將失敗。

所以一個醜陋的解決方法是修改(void)connection:(NSURLConnection __unused *)connection didFailWithError:(NSError *)errorAFURLConnectionOperation.m檢索緩存的響應,如果請求失敗,但只爲特定的緩存策略。

- (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; 
     } 
    } 
} 
+1

感謝這個答案,一個真正有用的總結。我認爲你需要改變你最後一個代碼示例的某些部分:在調用'[self finished]'之前應該應用添加,否則可能會調用'failure'塊。我也遇到過這種難以調試的競爭條件。 – 2013-12-06 13:25:02

+1

另外我應該說,我終於結束了使用SDURLCache。在離線模式下,NSURLCache不會在iOS 6上可靠地返回緩存響應。多麼痛苦。好的,我直接從蘋果公司獲得了這方面的消息。我一直在iOS Tech Talk上與Apple工程師交談。 *我:*「...我想使用NSURLCache,現在它支持磁盤緩存....」*蘋果公司的人:*「不,你不會!」然後他解釋說,它不是用於明確的離線場景,而是它旨在加速Safari,不應該用於手動下載。 - 那他們爲什麼不直接在文檔中寫出來?本來可以節省我幾個小時的生命。 – 2013-12-06 13:29:42

+0

@de。他有什麼解決方法嗎? – Ecarrion 2013-12-30 11:56:35

0

這聽起來像你想請求成功,即使高速緩存表示,數據已經過期,應該從服務器檢索。您可能有一些運氣設置某些請求的緩存策略(線上與線下的不同策略)您寧願使用陳舊數據而不是失敗。

NSMutableURLRequest -> setCachePolicy

看起來NSURLRequestReturnCacheDataDontLoad是離線模式所需的策略。

希望有幫助!

+0

但這意味着每次運行請求並相應地更改緩存策略時都必須檢查Internet連接,對嗎? – Ecarrion 2013-01-21 13:12:55

+0

查看AFHTTPClient中的setReachabilityStatusChangeBlock以監視網絡更改。也許你可以在那裏更改緩存策略?但是,您應該嘗試按照上面的說明設置緩存策略,然後查看它是否爲您提供了所需的「離線模式」行爲。如果你,你可以在線/離線之間查看你的切換機制。 – Dave 2013-01-21 15:07:02

+1

那麼,這是什麼工作? – Renetik 2013-03-02 22:33:05