今天在看HTTP協議,看到了response頭中的cache-control,於是就深入的研究了一下。發現了iOS中一個一直被我忽略的類——NSURLCache類。
NSURLCache
NSURLCache用於緩存網絡請求,也就是NSURLRequest,然后根據我們設置的NSURLCache策略進行相應的緩存。
首先介紹一下各種策略
| 策略 | 意義 |
| UseProtocolCachePolicy |
默認行為 |
| ReloadIgnoringLocalCacheData | 不使用緩存 |
| ReloadIgnoringLocalAndRemoteCacheData* | 我是認真地,不使用任何緩存 |
| ReturnCacheDataElseLoad | 使用緩存(不管它是否過期),如果緩存中沒有,那從網絡加載吧 |
| ReturnCacheDataDontLoad | 離線模式:使用緩存(不管它是否過期),但是不從網絡加載 |
| ReloadRevalidatingCacheData* | 在使用前去服務器驗證 |
其中ReloadIgnoringLocalAndRemoteCacheData和ReloadRevalidatingCacheData兩種是沒有實現的,可以不看。
在創建對request使用cache的時候會讓我們選擇以上的某種策略進行,也就是
+ (instancetype)requestWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval;
該方法讓我們設置策略和時間,然后request會根據策略和時間來進行相應的調度。
感受NSURLCache
這里使用默認的緩存策略ReturnCacheDataElseLoad緩存策略,
首先需要創建NSURLCache類
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:0 diskPath:nil]; [NSURLCache setSharedURLCache:URLCache];
1.這里可以看到,創建參數我們制定了 4 * 1024 * 1024的內存(4MB) ,沒有使用磁盤空間。
2.NSURLCache使用[NSURLCache sharedURLCache]創建默認的的緩存行為,默認為 4(MB) 內存和 20(MB)磁盤空間,這里我們使用自定義的,所以要setSharedCache。
然后創建request和connection進行請求
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://172.16.25.44/test1.php"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:3]; connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; [connection start];
實現NSURLConnectionDelegate協議
-(void)connectionDidFinishLoading:(NSURLConnection *)connection { NSLog(@"finish"); }
然后運行開一下請求,這里用的工具是Charles

可以看到只有一次請求,再看控制台輸出
2015-08-04 09:29:55.297 requestCache[19405:6375355] finish 2015-08-04 09:29:55.301 requestCache[19405:6375355] finish 2015-08-04 09:29:55.310 requestCache[19405:6375355] finish 2015-08-04 09:29:55.451 requestCache[19405:6375355] finish 2015-08-04 09:29:55.618 requestCache[19405:6375355] finish 2015-08-04 09:29:55.784 requestCache[19405:6375355] finish 2015-08-04 09:29:55.984 requestCache[19405:6375355] finish 2015-08-04 09:29:56.120 requestCache[19405:6375355] finish
所以說多次的請求只會進行一次請求,因為在內存中NSURLCache為我們緩存了一份response,一旦有同樣請求就會使用緩存。
緩存持久化
緩存如果設定本地磁盤就會為我們自動進行持久化,修改NSURLCache創建代碼
NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024 diskCapacity:20 * 1024 * 1024 diskPath:nil]; [NSURLCache setSharedURLCache:URLCache];
設置了20MB的本地磁盤,然后運行程序,進行請求發現還是請求一次沒有變化。但是在次運行程序進行請求就會發現,一次遠程請求也不會進行了!
打開沙盒,發現在 Library/Caches/bundleId+項目名/下面有三個文件

這不就是sqlite么!原來NSURLCache幫我們用sqlite將請求存入了數據庫,然后當有相同請求時就會調用緩存!
可以想到webView如果加載一個靜態頁面不用只用請求一次,並且在效果要更新的時候遠程請求會有多爽!
默認策略
默認策略是 UseProtocolCachePolicy 從字面上來看是說,使用協議緩存策略,但是什么是協議緩存策略呢?
在HTTP協議的response頭中,有一個字段是cache-control,由服務器來告訴客戶端如何使用緩存。
下面是一個response頭

可以看到cache-control指定的行為是public,max-age=5
這里先介紹一下各種指令

對應上表,可以看出了剛才響應頭是要求緩存所有內容,緩存5秒失效,5秒后還要請求遠程服務器。
對應PHP就是header("Cache-Control:public,max-age=5");
偽造響應
如果我們想讓一些請求,有特定的響應,我們可以自己來制作響應
NSLog(@"%@",[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]); NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://172.16.25.44/test1.php"] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:3]; NSURLCache * cache = [NSURLCache sharedURLCache]; NSData *contentData = [@"123" dataUsingEncoding:NSUTF8StringEncoding]; NSURLResponse *response = [[NSURLResponse alloc] initWithURL:[NSURL URLWithString:@"http://172.16.25.44/test1.php"] MIMEType:@"text/html" expectedContentLength:1000 textEncodingName:@"UTF-8"]; NSCachedURLResponse *cacheRespone = [[NSCachedURLResponse alloc] initWithResponse:response data:contentData]; [cache storeCachedResponse:cacheRespone forRequest:request]; connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; [connection start];
如上代碼,創建了一個針對@"http://172.16.25.44/test1.php"請求的響應,並且讓 cache 對該響應進行了存儲。
實現
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"%@",dataString); }
輸出結果如下
2015-08-04 09:48:58.825 requestCache[19503:6441561] 123 2015-08-04 09:48:58.826 requestCache[19503:6441561] finish 2015-08-04 09:48:58.983 requestCache[19503:6441561] 123 2015-08-04 09:48:58.984 requestCache[19503:6441561] finish 2015-08-04 09:48:59.167 requestCache[19503:6441561] 123 2015-08-04 09:48:59.167 requestCache[19503:6441561] finish 2015-08-04 09:48:59.334 requestCache[19503:6441561] 123 2015-08-04 09:48:59.335 requestCache[19503:6441561] finish
可以看到輸出的是我們自定義的123,而不是服務器返回的1。
修改響應內容
修改響應內容需要我們實現NSURLConnectionDataDelegate 協議並實現
-(NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse { NSMutableData *mutableData = [[cachedResponse data] mutableCopy]; //添加數據 NSCachedURLResponse *response = [[NSCachedURLResponse alloc] initWithResponse:cachedResponse.response data:mutableData]; return response; }
應為 NSCachedURLResponse 的屬性都是readonly的,所以我們想要添加內容就要創建一個可變副本增減內容。
