UIWebView內(nèi)容緩存

URL Loading System
在說 NSURLProtocol 之前需要對 URL Loading System 進(jìn)行說明,引用蘋果官方文檔對 URL Loading System 的一個解釋:

The URL loading system is a set of classes and protocols that allow your app to access content referenced by a URL. At the heart of this technology is the NSURL class, which lets your app manipulate URLs and the resources they refer to.

大致的意思就是 URL Loading System 是由一系列的 classProtocol 組成,而我們可以通過這些 classProtocol 來操作相關(guān)的 url ,其中處于核心的 class 就是 NSURL 悬而。
其中相關(guān)的 classProtocol 可以使用官方的一張圖來說明:

URL Loading System

當(dāng)然 URL Loading System 是由很多個方面組成的詳細(xì)的情況可以直接查詢蘋果的官方文檔 URL Loading System

URL loading system 原生已經(jīng)支持了http,https,file,ftp,data這些常見協(xié)議,當(dāng)然也允許我們定義自己的protocol去擴展峦朗,或者定義自己的協(xié)議份乒。當(dāng)URL loading system通過NSURLRequest對象進(jìn)行請求時,將會自動創(chuàng)建NSURLProtocol的實例(可以是自定義的)椰憋。這樣我們就有機會對該請求進(jìn)行處理厅克。官方文檔里面介紹得比較少,下面我們直接看如何自定義NSURLProtocol橙依,解讀一下 RNCachingURLProtocol這個開源庫的使用和原理证舟。

首先看一下源碼解析

//初始化
+ (void)initialize
{
  if (self == [RNCachingURLProtocol class])
  {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
      RNCachingSupportedSchemesMonitor = [NSObject new];
    });
    
    //設(shè)置支持的協(xié)議類型
    [self setSupportedSchemes:[NSSet setWithObject:@"http"]];
  }
}

//是否可以處理此次的網(wǎng)絡(luò)請求 yes 可以  no 丟棄
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
  // 判斷是否支持協(xié)議類型 和 request是否被處理過(防止遞歸調(diào)用)
  if ([[self supportedSchemes] containsObject:[[request URL] scheme]] &&
      ([request valueForHTTPHeaderField:RNCachingURLHeader] == nil))
  {
    return YES;
  }
  return NO;
}

//這邊可用干你想干的事情。窗骑。更改地址女责,或者設(shè)置里面的請求頭。创译。
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
  return request;
}

//設(shè)置請求內(nèi)容的緩存地址
- (NSString *)cachePathForRequest:(NSURLRequest *)aRequest
{
  // This stores in the Caches directory, which can be deleted when space is low, but we only use it for offline access
  NSString *cachesPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
  NSString *fileName = [[[aRequest URL] absoluteString] sha1];//這里只是一個用SHA1算法將字符串加密的category

  return [cachesPath stringByAppendingPathComponent:fileName];
}

- (void)startLoading
{
    //判斷是否已經(jīng)緩存過
  if (![self useCache]) {
    NSMutableURLRequest *connectionRequest = 
#if WORKAROUND_MUTABLE_COPY_LEAK
      [[self request] mutableCopyWorkaround];
#else
      [[self request] mutableCopy];
#endif
    // 打一下標(biāo)記
    [connectionRequest setValue:@"" forHTTPHeaderField:RNCachingURLHeader];
    NSURLConnection *connection = [NSURLConnection connectionWithRequest:connectionRequest
                                                                delegate:self];
    [self setConnection:connection];
  }
  else {
    //創(chuàng)建緩存對象
    RNCachedData *cache = [NSKeyedUnarchiver unarchiveObjectWithFile:[self cachePathForRequest:[self request]]];
    if (cache) {
      NSData *data = [cache data];
      NSURLResponse *response = [cache response];
      NSURLRequest *redirectRequest = [cache redirectRequest];
      if (redirectRequest) {
          //重復(fù)訪問了同一個請求
        [[self client] URLProtocol:self wasRedirectedToRequest:redirectRequest redirectResponse:response];
      } else {
          //處理請求
        [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; // we handle caching ourselves.
        [[self client] URLProtocol:self didLoadData:data];
        [[self client] URLProtocolDidFinishLoading:self];
      }
    }
    else {
      [[self client] URLProtocol:self didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotConnectToHost userInfo:nil]];
    }
  }
}

//緩存請求的內(nèi)容 
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
{
// Thanks to Nick Dowell https://gist.github.com/1885821
  if (response != nil) {
      NSMutableURLRequest *redirectableRequest =
#if WORKAROUND_MUTABLE_COPY_LEAK
      [request mutableCopyWorkaround];
#else
      [request mutableCopy];
#endif
      //標(biāo)記為nil 防止遞歸調(diào)用 canonicalRequestForRequest
    [redirectableRequest setValue:nil forHTTPHeaderField:RNCachingURLHeader];

    NSString *cachePath = [self cachePathForRequest:[self request]];
    RNCachedData *cache = [RNCachedData new];
    [cache setResponse:response];
    [cache setData:[self data]];
    [cache setRedirectRequest:redirectableRequest];
    [NSKeyedArchiver archiveRootObject:cache toFile:cachePath];
    [[self client] URLProtocol:self wasRedirectedToRequest:redirectableRequest redirectResponse:response];
    return redirectableRequest;
  } else {
    return request;
  }
}

#pragma mark - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
  [[self client] URLProtocol:self didLoadData:data];
  [self appendData:data];
}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
  [[self client] URLProtocol:self didFailWithError:error];
  [self setConnection:nil];
  [self setData:nil];
  [self setResponse:nil];
}

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
  [self setResponse:response];
  [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];  // We cache ourselves.
}

注意點:
每次只能只有一個protocol進(jìn)行處理抵知,如果有多個自定義protocol,系統(tǒng)將采取你registerClass的倒序進(jìn)行調(diào)用,一旦你需要對這個請求進(jìn)行處理刷喜,那么接下來的所有相關(guān)操作都需要這個protocol進(jìn)行管理残制。
一定要注意標(biāo)記請求,不然你會無限的循環(huán)下去掖疮。初茶。。因為一旦你需要處理這個請求浊闪,那么系統(tǒng)會創(chuàng)建你這個protocol的實例恼布,然后你自己又開啟了connection進(jìn)行請求的話,又會觸發(fā)URL Loading system的回調(diào)搁宾。系統(tǒng)給我們提供了+ (void)setProperty:(id)value forKey:(NSString *)key inRequest:(NSMutableURLRequest *)request;和+ (id)propertyForKey:(NSString *)key inRequest:(NSURLRequest *)request;這兩個方法進(jìn)行標(biāo)記和區(qū)分折汞。
大家在使用的時候只需要在Appdelegate注冊一下,然后緩存路徑自己處理一下就可以了。

[NSURLProtocol registerClass:[RNCachingURLProtocol class]];

歡迎討論!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末猛铅,一起剝皮案震驚了整個濱河市字支,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奸忽,老刑警劉巖堕伪,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異栗菜,居然都是意外死亡欠雌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進(jìn)店門疙筹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來富俄,“玉大人,你說我怎么就攤上這事而咆』舯龋” “怎么了?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵暴备,是天一觀的道長悠瞬。 經(jīng)常有香客問我,道長涯捻,這世上最難降的妖魔是什么浅妆? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮障癌,結(jié)果婚禮上凌外,老公的妹妹穿的比我還像新娘。我一直安慰自己涛浙,他們只是感情好康辑,可當(dāng)我...
    茶點故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布摄欲。 她就那樣靜靜地躺著,像睡著了一般疮薇。 火紅的嫁衣襯著肌膚如雪蒿涎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天惦辛,我揣著相機與錄音,去河邊找鬼仓手。 笑死胖齐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嗽冒。 我是一名探鬼主播呀伙,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼添坊!你這毒婦竟也來了剿另?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤贬蛙,失蹤者是張志新(化名)和其女友劉穎雨女,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阳准,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡氛堕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了野蝇。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讼稚。...
    茶點故事閱讀 40,861評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖绕沈,靈堂內(nèi)的尸體忽然破棺而出锐想,到底是詐尸還是另有隱情,我是刑警寧澤乍狐,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布赠摇,位于F島的核電站,受9級特大地震影響澜躺,放射性物質(zhì)發(fā)生泄漏蝉稳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一掘鄙、第九天 我趴在偏房一處隱蔽的房頂上張望耘戚。 院中可真熱鬧,春花似錦操漠、人聲如沸收津。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撞秋。三九已至长捧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間吻贿,已是汗流浹背串结。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舅列,地道東北人肌割。 一個月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像帐要,于是被迫代替她去往敵國和親把敞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,860評論 2 361

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理榨惠,服務(wù)發(fā)現(xiàn)奋早,斷路器,智...
    卡卡羅2017閱讀 134,716評論 18 139
  • 概覽 緩存組件應(yīng)該說是每個客戶端程序必備的核心組件赠橙,試想對于每個界面的訪問都必須重新請求勢必降低用戶體驗耽装。但是如何...
    默默_David閱讀 1,938評論 1 9
  • 【一】 距離我走進(jìn)2018年的考研戰(zhàn)場還有140天剂邮,而我踏上考研征程也該有一個月了,邊走邊摸索方法横侦,邊學(xué)邊總結(jié)經(jīng)驗...
    偏執(zhí)girl閱讀 216評論 2 3
  • 多愁善感挥萌,柔情似水的直子 野性未脫,活潑迷人的綠子 孤獨的都市少年枉侧,徘徊于兩位少女之間
    絳谷千紘閱讀 223評論 0 0
  • 雖然我不相信命運的安排但我還是承認(rèn)命中注定這一個說法引瀑。 我們認(rèn)識是在小縣城的鎮(zhèn)級初中,同一個班級榨馁。還記得那時的她扎...
    仫白閱讀 231評論 2 1