今日剛上班,就收到了一個(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è)黑暗的小角落。
?
果然,在這里看到了這個(gè)不起眼確似曾相識(shí)的貨,Cache.db,然后順便把這貨砸開(kāi)看看。
看著很有料吧,繼續(xù)爆。
?
東西果然不少呢,其中的request_key就是你app中使用請(qǐng)求的url,還包括一些第三方庫(kù)的請(qǐng)求。
?
請(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ù)一成不變) 最好果斷地禁止使用緩存。