NSURLCache 可能被你忽略的

今日剛上班,就收到了一個(gè)比較詭異的問(wèn)題。

客戶(hù)反饋,app上面天氣界面顯示的天氣,和他本人從其他app得到的天氣溫度范圍不一致而且差距較大。
獲取天氣的接口
(http://api.map.baidu.com/telematics/v3/weather) 。趕緊打開(kāi)Xcode 親自獲取了一下發(fā)現(xiàn)我們這邊獲取的確實(shí)不一樣。這就納悶了,百度不該坑我啊,仔細(xì)一看,原來(lái)得到天氣日期是昨天的。但是呢,我在瀏覽器打開(kāi)url,獲取到的天氣又是正常的。這是咋回事呢,容我三思。三思之后,我覺(jué)得可能是緩存造成的,但獲取天氣這里,我們并沒(méi)有主動(dòng)緩存天氣數(shù)據(jù)。

因?yàn)檎?qǐng)求是用AFNetworking,所有就又點(diǎn)開(kāi)了AFNetworking的源碼。

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
                                 URLString:(NSString *)URLString
                                parameters:(id)parameters
                                     error:(NSError *__autoreleasing *)error
{
    NSParameterAssert(method);
    NSParameterAssert(URLString);

    NSURL *url = [NSURL URLWithString:URLString];

    NSParameterAssert(url);

    NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
    mutableRequest.HTTPMethod = method;

    for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
        if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
            [mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
        }
    }

    mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

    return mutableRequest;
}

AFHTTPRequestSerializerObservedKeyPaths() 是什么呢?繼續(xù)看

static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
    static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
    });

    return _AFHTTPRequestSerializerObservedKeyPaths;
}

原來(lái)是一個(gè)方法的字符串?dāng)?shù)組。在這里,每一個(gè)mutableRequest都會(huì)設(shè)置一個(gè)緩存策略,且有一個(gè)默認(rèn)值, NSURLRequestUseProtocolCachePolicy。

/**
 The cache policy of created requests. `NSURLRequestUseProtocolCachePolicy` by default.
 
 @see NSMutableURLRequest -setCachePolicy:
 */
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;
typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
    NSURLRequestUseProtocolCachePolicy = 0,

    NSURLRequestReloadIgnoringLocalCacheData = 1,
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented
    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,

    NSURLRequestReturnCacheDataElseLoad = 2,
    NSURLRequestReturnCacheDataDontLoad = 3,

    NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented
};

分別看看這些緩存代表什么意思呢?

  • NSURLRequestUseProtocolCachePolicy: 對(duì)特定的 URL 請(qǐng)求使用網(wǎng)絡(luò)協(xié)議中實(shí)現(xiàn)的緩存邏輯。這是默認(rèn)的策略。
  • NSURLRequestReloadIgnoringLocalCacheData:數(shù)據(jù)需要從原始地址加載。不使用現(xiàn)有緩存。
  • NSURLRequestReloadIgnoringLocalAndRemoteCacheData:不僅忽略本地緩存,同時(shí)也忽略代理服務(wù)器或其他中間介質(zhì)目前已有的、協(xié)議允許的緩存。
  • NSURLRequestReturnCacheDataElseLoad:無(wú)論緩存是否過(guò)期,先使用本地緩存數(shù)據(jù)。如果緩存中沒(méi)有請(qǐng)求所對(duì)應(yīng)的數(shù)據(jù),那么從原始地址加載數(shù)據(jù)。
  • NSURLRequestReturnCacheDataDontLoad:無(wú)論緩存是否過(guò)期,先使用本地緩存數(shù)據(jù)。如果緩存中沒(méi)有請(qǐng)求所對(duì)應(yīng)的數(shù)據(jù),那么放棄從原始地址加載數(shù)據(jù),請(qǐng)求視為失敗(即:“離線(xiàn)”模式)。
  • NSURLRequestReloadRevalidatingCacheData:從原始地址確認(rèn)緩存數(shù)據(jù)的合法性后,緩存數(shù)據(jù)就可以使用,否則從原始地址加載。

此前我一直還認(rèn)為NSURLCache不就是內(nèi)存緩存嘛,好辦,app關(guān)掉重新打開(kāi)就可以了吧。結(jié)果,濤聲依舊,還是有緩存。
繼續(xù)思考,我決定找找這個(gè)神秘的幕后黑手。
接下來(lái),我就找了下NSURLCache 的屬性,發(fā)現(xiàn)了currentDiskUsage,diskCapacity 這個(gè)2個(gè)屬性,難道NSURLCache 還有磁盤(pán)緩存?
繼續(xù)仔細(xì)探索,發(fā)現(xiàn)這段文字。

NSURLCache 為您的應(yīng)用的 URL 請(qǐng)求提供了內(nèi)存中以及磁盤(pán)上的綜合緩存機(jī)制。 作為基礎(chǔ)類(lèi)庫(kù) URL 加載系統(tǒng) 的一部分,任何通過(guò) NSURLConnection 加載的請(qǐng)求都將被 NSURLCache 處理。

真是既有內(nèi)存緩存又有磁盤(pán)緩存。接下來(lái),我就打印并觀察currentDiskUsage、currentMemoryUsage 這2個(gè)值。

    NSURLCache  * cache =[NSURLCache sharedURLCache];
    NSLog(@"cache.currentDiskUsage: %ld" ,(unsigned long)cache.currentDiskUsage);
    NSLog(@"cache.currentMemoryUsage: %ld" ,(unsigned long)cache.currentMemoryUsage);

過(guò)程中,發(fā)現(xiàn)memoryCapacity 默認(rèn)值512KB currentMemoryUsage 默認(rèn)值10MB,可以自己設(shè)置大小。 app殺掉重新打開(kāi)currentMemoryUsage的確會(huì)為0。使用過(guò)程中currentMemoryUsage 會(huì)慢慢增加,操作了好一陣才到10KB左右,而currentDiskUsage增加非常兇猛,已經(jīng)到了100多KB。

接著我就想,currentDiskUsage這貨在在哪呢,難道是在沙盒中那個(gè)Caches中某個(gè)黑暗的小角落。

1.png

?
果然,在這里看到了這個(gè)不起眼確似曾相識(shí)的貨,Cache.db,然后順便把這貨砸開(kāi)看看。

2.png

看著很有料吧,繼續(xù)爆。

3.png

?
東西果然不少呢,其中的request_key就是你app中使用請(qǐng)求的url,還包括一些第三方庫(kù)的請(qǐng)求。

4.png

?
請(qǐng)求返回的數(shù)據(jù)全在這里了。

最后,知道了問(wèn)題出在哪里,就好改bug了。

afManager.requestSerializer.cachePolicy=NSURLRequestReloadIgnoringLocalCacheData;

改完以后,數(shù)據(jù)果然是最新的了,但是為毛和瀏覽器中的天氣數(shù)據(jù)還不一樣呢?
Safari->開(kāi)發(fā)->清空緩存 重新加載 ,這下都一樣了!

如果你的請(qǐng)求實(shí)時(shí)性相對(duì)較高,而且每次url一模一樣(參數(shù)一成不變) 最好果斷地禁止使用緩存。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容