溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

iOS教程:詳解iOS多圖下載的緩存機(jī)制

發(fā)布時(shí)間:2020-07-12 21:49:07 來源:網(wǎng)絡(luò) 閱讀:690 作者:IT大贏家 欄目:移動(dòng)開發(fā)

  ios教程,ios的干貨一直來不及給大家分享,小編也是一直在忙啊!今天給大家獻(xiàn)上ios:詳解iOS多圖下載的緩存機(jī)制

  1. 需求點(diǎn)是什么?

  這里所說的多圖下載,就是要在tableview的每一個(gè)cell里顯示一張圖片,而且這些圖片都需要從網(wǎng)上下載。

  2. 容易遇到的問題

  如果不知道或不使用異步操作和緩存機(jī)制,那么寫出來的代碼很可能會(huì)是這樣:

  cell.textLabel.text = app.name;

  cell.detailTextLabel.text = app.download;NSData *p_w_picpathData = [NSData dataWithContentsOfURL:app.url];

  cell.p_w_picpathView.p_w_picpath = [UIImage p_w_picpathWithData:p_w_picpathData];

  這樣寫有什么后果呢?

  后果1:不可避免的卡頓(因?yàn)闆]有異步下載操作)

  dataWithContentsOfURL:是耗時(shí)操作,將其放在主線程會(huì)造成卡頓。如果圖片很多,圖片很大,而且網(wǎng)絡(luò)情況不好的話肯定會(huì)卡出翔!

  后果2:同一圖片重復(fù)下載,耗費(fèi)流量和系統(tǒng)開銷(因?yàn)闆]有建立緩存機(jī)制)

  由于沒有緩存機(jī)制,即使下載完成并顯示了當(dāng)前cell的圖片,但是當(dāng)該cell再一次需要顯示的時(shí)候還是會(huì)下載它所對(duì)應(yīng)的圖片:耗費(fèi)了下載流量,而且還導(dǎo)致重復(fù)操作。

  很顯然,要達(dá)到Tableview滾動(dòng)的如絲滑般的享受必須二者兼得才可以,具體怎么做呢?

  3. 解決方案

  1.先看一下解決方案的流程圖

  iOS教程:詳解iOS多圖下載的緩存機(jī)制

  要想快速看懂此圖,需要先了解該流程所需的所有數(shù)據(jù)源:

  1. 圖片的URL:因?yàn)槊繌垐D片對(duì)應(yīng)的URL都是唯一的,所以我們可以通過它來建立圖片緩存和下載操作的緩存的鍵,以及拼接沙盒緩存的路徑字符串。

  2. 圖片緩存(字典):存放于內(nèi)存中;鍵為圖片的URL,值為UIImage對(duì)象。作用:讀取速度快,直接使用UIImage對(duì)象。

  3. 下載操作緩存(字典):存放與內(nèi)存中,鍵為圖片的URL,值為NSBlockOperation對(duì)象。作用:用來避免對(duì)于同一張圖片還要開啟多個(gè)下載線程。

  4. 沙盒緩存(文件路徑對(duì)應(yīng)NSData):存放于磁盤中,位于Cache文件夾內(nèi),路徑為“Cache/圖片URL的最后的部分”,值為NSData對(duì)象(將UIImage轉(zhuǎn)化為NSData才能寫入磁盤里)。作用:程序斷網(wǎng),再次啟動(dòng)也可以直接在磁盤中拿到圖片。

  2. 再看一下解決方案的代碼

  2.1圖片緩存,下載操作緩存,沙盒緩存路徑

  /**

  * 存放所有下載完的圖片

  */@property (nonatomic, strong) NSMutableDictionary *p_w_picpaths;/**

  * 存放所有的下載操作(key是url,value是operation對(duì)象)

  */@property (nonatomic, strong) NSMutableDictionary *operations;/**

  * 拼接Cache文件夾的路徑與url最后的部分,合并成唯一約定好的緩存路徑

  */#define CachedImageFile(url) [[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:[url lastPathComponent]]

  2.2 圖片下載之前的查詢緩存部分:

  //先從p_w_picpaths緩存中取出圖片url對(duì)應(yīng)的UIImage

  UIImage *p_w_picpath = self.p_w_picpaths[app.icon]; if (p_w_picpath) { //存在:說明圖片已經(jīng)下載成功,并緩存成功)

  cell.p_w_picpathView.p_w_picpath = p_w_picpath;

  } else { // 不存在:說明圖片并未下載成功過,或者成功下載但是在p_w_picpaths里緩存失敗,需要在沙盒里尋找對(duì)于的圖片

  // 獲得url對(duì)于的沙盒緩存路徑

  NSString *file = CachedImageFile(app.icon); // 先從沙盒中取出圖片

  NSData *data = [NSData dataWithContentsOfFile:file]; if (data) { //data不為空,說明沙盒中存在這個(gè)文件

  cell.p_w_picpathView.p_w_picpath = [UIImage p_w_picpathWithData:data];

  } else {// 反之沙盒中不存在這個(gè)文件

  // 在下載之前顯示占位圖片

  cell.p_w_picpathView.p_w_picpath = [UIImage p_w_picpathNamed:@"placeholder"];// 下載圖片

  [self download:app.icon indexPath:indexPath];

  }

  }

  2.3 圖片的下載部分:

  /**

  * 下載圖片

  * @param p_w_picpathUrl 圖片的url

  */- (void)download:(NSString *)p_w_picpathUrl indexPath:(NSIndexPath *)indexPath

  { // 取出當(dāng)前圖片url對(duì)應(yīng)的下載操作(operation對(duì)象)

  NSBlockOperation *operation = self.operations[p_w_picpathUrl]; if (operation) return; // 創(chuàng)建操作,下載圖片

  __weak typeof(self) appsVc = self;

  operation = [NSBlockOperation blockOperationWithBlock:^{ NSURL *url = [NSURL URLWithString:p_w_picpathUrl]; NSData *data = [NSData dataWithContentsOfURL:url];// 下載

  UIImage *p_w_picpath = [UIImage p_w_picpathWithData:data]; // NSData -> UIImage

  // 回到主線程

  [[NSOperationQueue mainQueue] addOperationWithBlock:^{ if (p_w_picpath) { // 如果存在圖片(下載完成),存放圖片到圖片緩存字典 中

  appsVc.p_w_picpaths[p_w_picpathUrl] = p_w_picpath; //將圖片存入沙盒中

  //1. 先將圖片轉(zhuǎn)化為NSData

  NSData *data = UIImagePNGRepresentation (p_w_picpath); //2. 再生成緩存路徑

  [data writeToFile:CachedImageFile(p_w_picpathUrl) atomically:YES];

  } // 從字典中移除下載操作 (保證下載失敗后, 能重新下載)

  [appsVc.operations removeObjectForKey:p_w_picpathUrl]; // 刷新當(dāng)前表格,減少系統(tǒng)開銷

  [appsVc.tableView reloadRowsAtIndexPaths:@ [indexPath] withRowAnimation:UITableViewRowAnimationNone];

  }];

  }]; // 添加下載操作到隊(duì)列中

  [self.queue addOperation:operation]; // 將當(dāng)前下載操作添加到下載操作緩存中 (為了解決重復(fù)下載)

  self.operations[p_w_picpathUrl] = operation;

  }

  3. 有哪些點(diǎn)是值得注意的?

  要說值得注意的地方,還是離不開對(duì)于緩存內(nèi)容的添加和刪除操作。

  3.1 關(guān)于圖片緩存:

  很簡單,成功下載,拿到了圖片,就將圖片添加到圖片緩存中;下載失敗,什么都不做,反正沒有圖。在這種機(jī)制下,就沒有刪除緩存里某個(gè)圖片項(xiàng)的情況,因?yàn)閳D片緩存永遠(yuǎn)不會(huì)出現(xiàn)重復(fù)添加多個(gè)相同圖片的情況,緩存中只要有一張對(duì)應(yīng)的圖,就直接拿去用了,不會(huì)去再下載了。

  3.2 關(guān)于沙盒緩存:

  同樣地,對(duì)于沙盒緩存也是一個(gè)道理:有圖就將其轉(zhuǎn)化為NSData,寫入磁盤,并對(duì)應(yīng)唯一的路徑,沒有圖就不寫。所以即使是要下載相同的圖片,因?yàn)楫?dāng)前url對(duì)應(yīng)的沙盒路徑已經(jīng)存在文件了,所以直接拿就可以了,不會(huì)再下載。

  但是!

  下載操作緩存是不同的!

  3.3 關(guān)于下載操作緩存

  我們需要在下載回調(diào)完成后,立即將當(dāng)前的下載操作從下載操作緩存中刪去!

  因?yàn)橐苊庀螺d失敗后,無法再次下載的情況的發(fā)生!

  為什么呢?

  注意一下將下載操作加入到下載操作緩存的時(shí)機(jī):

  是在下載開始的那一刻而不是下載成功的那一刻!

  如果在下載開始的那一刻加入到緩存中的話,這個(gè)緩存信息就包括兩個(gè)情況:下載成功和下載失?。?/p>

  如果未來下載成功了,那么我們就不會(huì)來到判斷當(dāng)前下載操作是否在下載操作緩存這一步,在這之前直接就可以拿圖去用了,下載操作是否存在下載操作緩存里并沒有什么影響。

  但是!如果未來下載失敗了,那就肯定不會(huì)有對(duì)應(yīng)的圖片緩存和沙盒緩存,也就肯定會(huì)來到判斷當(dāng)前的下載操作是否在下載操作緩存里這一步。不幸的是,因?yàn)闆]有被刪去,它是存在的。存在的話就不做任何其他操作,放任自流,導(dǎo)致曾經(jīng)下載失敗的圖片永遠(yuǎn)不會(huì)再次下載。

  忘了那段代碼了么?回看一下代碼(看我多好):

  NSBlockOperation *operation = self.operations[p_w_picpathUrl]; if (operation) return;//轉(zhuǎn)身就走,毫不留情

  因此,無論下載成功或是失敗,在圖片下載的回調(diào)里都要將當(dāng)前的下載操作從下載操作隊(duì)列中移走:用來保證如果下載失敗了,就可以重新開啟對(duì)應(yīng)的下載

  操作進(jìn)行下載,邏輯上更加嚴(yán)謹(jǐn)。

  4. 最后的話

  異步+緩存這兩個(gè)機(jī)制雙劍合璧的話會(huì)對(duì)程序新能帶來很大的改觀。這應(yīng)該app開發(fā)進(jìn)階的必經(jīng)之路。

  小碼哥講述的這套流程還算比較完整的了,更重要的還是學(xué)習(xí)其中的思想:

  將緩存分級(jí):內(nèi)存緩存,沙盒緩存,下載操作緩存。

  而且還要經(jīng)常使用二分法,將我們的邏輯考慮得滴水不漏。

  如果我們沒有認(rèn)識(shí)到將下載操作添加到下載操作緩存的時(shí)機(jī)是包含下載成功和下載失敗兩個(gè)情況,那么就不會(huì)考慮到即時(shí)要將下載操作從下載操作緩存中刪去的操作,很容易引起bug。所以在以后的開發(fā)中,成功和失敗兩個(gè)情況都要考慮進(jìn)去,也就是說有if一定要有else!


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI