一款輕量級(jí)的 iOS 圖像緩存
來源:易賢網(wǎng) 閱讀:1248 次 日期:2015-05-08 14:39:04
溫馨提示:易賢網(wǎng)小編為您整理了“一款輕量級(jí)的 iOS 圖像緩存”,方便廣大網(wǎng)友查閱!

我們的 iOS 應(yīng)用都包含了大量的圖像。創(chuàng)建富有吸引力的視圖,主要依賴于大量的裝飾圖片,所有這些首先必須從遠(yuǎn)程服務(wù)器獲取。如果每次打開應(yīng)用都要從服務(wù)器一次又一次的獲取每個(gè)圖像,那么用戶體驗(yàn)肯定達(dá)不到好的效果,所以本地緩存遠(yuǎn)程圖像是非常有必要的。

版本1-尋找一張圖片,并從磁盤上讀取它

我們的第一個(gè)圖片緩存是簡單但是有效的。從緩存中尋找每一個(gè)我們?cè)?jīng)訪問過的圖片,用遠(yuǎn)程的URL作為緩存的鍵值。如果本地的磁盤緩存時(shí)有效的,從磁盤中讀取文件并創(chuàng)建UIImage,并立即返回。如果再磁盤上沒有找到文件,異步的從遠(yuǎn)程URL獲取文件,緩存到磁盤,然后返回一個(gè)新建的UIImage。

目前這對(duì)于我們的使用是完全滿足了。但是它有一個(gè)不必要的弱點(diǎn):每次緩存請(qǐng)求都需要從磁盤讀取圖片,性能都消耗在對(duì)磁盤的訪問和圖片文件的解碼上了。

版本 2 - 內(nèi)存緩存

謝天謝地,蘋果的UIImage有內(nèi)置的內(nèi)存緩存。所以,你只需修改一行代碼,圖片就能從磁盤緩存改為內(nèi)存緩存。

當(dāng)你使用imageNamed:獲取UIImage的時(shí)候,它第一步就是檢查自己的內(nèi)存看看是否已經(jīng)加載過這個(gè)圖片。如果這樣,你不用任何系統(tǒng)花銷就能得到一個(gè)UIImage實(shí)例。所以把原本這樣的寫法替換掉:

return [UIImage imageWithContentsOfFile:[self absolutePathForURL:url]];

我們可以零花銷去訪問內(nèi)存緩存,用下面的代碼就可以了:

return [UIImage imageNamed:[self relativePathForURL:url]];

UIImage 會(huì)查找它的內(nèi)存緩存,如果找到,就會(huì)零消耗地返回這個(gè)照片. 如果沒有找到,這張照片就會(huì)從磁盤加載,消耗一定的系統(tǒng)性能。

版本3 —— 獲取隊(duì)列、數(shù)據(jù)預(yù)取和變量催促

當(dāng)改進(jìn)應(yīng)用程序設(shè)計(jì)時(shí)我們渴望更多的圖片元素,渴望更炫的畫面,更大的圖片,更多的圖片。

讓這些大圖盡可能快的顯示在屏幕上對(duì)用戶體驗(yàn)來說是至關(guān)重要的,而只是簡單地在每次需要顯示的時(shí)候去緩存當(dāng)中抓取圖片數(shù)據(jù)是不能夠解決問題的。大圖需要更多的時(shí)間才能從網(wǎng)絡(luò)上加載回來,并且一次請(qǐng)求太多的圖片會(huì)導(dǎo)致所有圖片都來不及加載。需要仔細(xì)考慮什么時(shí)候去檢查緩沖當(dāng)中有無圖片數(shù)據(jù)以及什么時(shí)候從網(wǎng)絡(luò)上抓取圖片。我們需要預(yù)緩存和抓取列陣。

快速隊(duì)列和慢速隊(duì)列

我們?cè)O(shè)置了兩個(gè)隊(duì)列,一個(gè)串行,一個(gè)并行。在屏幕上被迫切要求的圖片進(jìn)入并行隊(duì)列(fastQueue),可能晚點(diǎn)才需要的圖片進(jìn)入串行隊(duì)列(slowQueue)。

就UITableView的實(shí)現(xiàn)而言,這意味著在屏幕上的表格單元從fastQueue獲取圖片, 每個(gè)關(guān)閉的屏幕行的圖片從slowQueue預(yù)加載。

現(xiàn)在不需要處理圖片

假設(shè)我們要從服務(wù)器上請(qǐng)求包含30條事件的一頁資訊回來,一旦這些內(nèi)容請(qǐng)求回來時(shí)我們就可以排隊(duì)等待預(yù)取其中的每一張圖。

- (void)pageLoaded:(NSArray *)newEvents {

for (SGEvent *event in newEvents) {

[SGImageCache slowGetImageForURL:event.imageURL thenDo:nil];

}

}

slowGetImageForURL:這個(gè)方法將圖片添加到slowQueue這個(gè)隊(duì)列當(dāng)中,允許它們?cè)诓蛔枞W(wǎng)絡(luò)通信的前提下被一張一張的取出來。

thenDo:這個(gè)代碼塊在這里是沒有被實(shí)現(xiàn),是因?yàn)槲覀兡壳斑€不需要對(duì)圖片做任何事情。所有我們需要做的就是確保它們?cè)诒镜卮疟P緩存當(dāng)中,并且隨時(shí)準(zhǔn)備在屏幕上滑動(dòng)表格時(shí)來使用。

現(xiàn)在就要處理圖片

顯示在屏幕上的表格希望立即顯示它們的圖片,所以在table cell子類當(dāng)中實(shí)現(xiàn):

- (void)setEvent:(SGEvent *)event {

__weak SGEventCell *me = self;

[SGImageCache getImageForURL:event.imageURL thenDo:^(UIImage *image) {

me.imageView.image = image; }

];

}

getImageForURL:這個(gè)方法將抓取圖片的過程添加到fastQueue這個(gè)隊(duì)列當(dāng)中,意味著只要iOS系統(tǒng)允許,它們會(huì)并行被地執(zhí)行。如果抓取圖片的過程已經(jīng)存在于slowQueue隊(duì)列當(dāng)中,它會(huì)被移動(dòng)到fastQueue隊(duì)列中,從而避免重復(fù)請(qǐng)求。

一直異步

等等,getImageForURL:不是一個(gè)異步方法嗎?如果你明知道圖片已經(jīng)在緩存中,但是卻不想在主線程上立即使用它嗎?直覺告訴你那是錯(cuò)誤的。

從磁盤上加載圖片太費(fèi)資源,同樣解壓圖片也會(huì)費(fèi)很多資源??梢栽诨瑒?dòng)的過程當(dāng)中進(jìn)行配置和添加表格,這最后一件你想在滑動(dòng)表格時(shí)做的事是很危險(xiǎn)地,因?yàn)樗鼤?huì)阻塞主線程,會(huì)有卡頓的現(xiàn)象出現(xiàn)。

使用getImageForURL:可以讓磁盤加載的動(dòng)作脫離主線程,于是當(dāng)thenDo:這個(gè)用于收尾工作的代碼塊執(zhí)行的時(shí)候它已經(jīng)有了一個(gè)UIImage實(shí)例,從而不會(huì)有滑動(dòng)卡頓的危險(xiǎn)。如果圖片已經(jīng)存在于本地緩存當(dāng)中,用于收尾工作的代碼塊會(huì)在下一次運(yùn)行周期執(zhí)行,并且用戶不會(huì)注意到兩者之間的差別。他們會(huì)注意到的是滑動(dòng)不會(huì)卡頓了。

現(xiàn)在,不需要你快速執(zhí)行

如果用戶很快的滑動(dòng)表格到底部,幾十或幾百個(gè)表格單元會(huì)出現(xiàn)在屏幕上,并向fastQueue請(qǐng)求圖片數(shù)據(jù),然后很快地從屏幕上消失。突然間這個(gè)并行地隊(duì)列會(huì)將大量實(shí)際上不再需要的圖片請(qǐng)求充斥進(jìn)網(wǎng)絡(luò)。當(dāng)用戶最終停止滑動(dòng)時(shí),那些當(dāng)前屏幕上相應(yīng)的表格單元視圖會(huì)將它們的圖片請(qǐng)求至于那些并不急需的請(qǐng)求后面,因此網(wǎng)絡(luò)阻塞了。

這就是 wheremoveTaskToSlowQueueForURL:這個(gè)方法的產(chǎn)生的原因.

// a table cell is going off screen-

(void)tableView:(UITableView *)table

didEndDisplayingCell:(UITableViewCell *)cell

forRowAtIndexPath:(NSIndexPath*)indexPath {

// we don't need it right now, so move it to the slow queue

[SGImageCache moveTaskToSlowQueueForURL:[[(id)cell event] imageURL]];}

這確保在fastQueue中的只會(huì)有真正需要被快速執(zhí)行的任務(wù)。任何以前認(rèn)為需要快速執(zhí)行但現(xiàn)在不需要的任務(wù)會(huì)被移至slowQueue中。

重點(diǎn)和選擇

已經(jīng)有相當(dāng)多的iOS圖片緩存庫。它們中一些庫只針對(duì)某些應(yīng)用場景,一些庫提供了不同場景一定的可擴(kuò)展性。我們的庫即沒有專門針對(duì)某些應(yīng)用場景,也沒有太多大而全的特性。針對(duì)我們的用戶我們有三類基本的重點(diǎn):

重點(diǎn) 1: 最好的幀率

很多的庫都非常專注在這一點(diǎn)上,使用一些高度定制和復(fù)雜的方法,盡管基準(zhǔn)沒有決定性地顯示這樣有效。我們發(fā)現(xiàn)最好的幀率由這些決定:

將對(duì)磁盤的訪問(并且?guī)缀跗渌乃校┟撾x主線程。

使用UIImage的內(nèi)存緩存來避免不必要的磁盤訪問和圖片解壓。

重點(diǎn) 2: 讓最最重要的圖片優(yōu)先顯示

大多數(shù)的庫都考慮讓隊(duì)列管理成為別人關(guān)心的事。對(duì)于我們的應(yīng)用,這幾乎是最重要的點(diǎn)。

讓正確的圖片在正確的時(shí)間顯示在屏幕上可以歸結(jié)為一個(gè)簡單的問題:“我們現(xiàn)在就需要它顯示還是過一會(huì)兒?”。那些需要立即顯示的圖片是并行加載地,而其它所有東西都被添加到串行隊(duì)列中。所有之前急迫的事但現(xiàn)在不急迫的話就會(huì)從fastQueue分到slowQueue中。并且當(dāng)fastQueue在工作時(shí),slowQueue是處于掛起狀態(tài)的。

這讓那些急需顯示的圖片可以單獨(dú)訪問網(wǎng)絡(luò),同時(shí)也確保了一張非急需顯示的圖片可以在過一會(huì)成為一張急需顯示的圖片,因?yàn)樗呀?jīng)存到了緩存當(dāng)中,隨時(shí)準(zhǔn)備用于顯示。

重點(diǎn) 3: 盡可能簡單的API

大多數(shù)庫都做到了這一點(diǎn)。許多庫為了隱藏細(xì)節(jié)內(nèi)容而提供了UIImageView的分類,并且許多庫讓抓取一張圖片的流程變得盡可能的便利。針對(duì)我們經(jīng)常做的三件事,我們的庫選定了三個(gè)主要的方法:

快速抓到一張圖

__weak SGEventCell *me = self;[SGImageCache getImageForURL:event.imageURL thenDo:^(UIImage *image) { me.imageView.image = image;}];

排隊(duì)等待一張我們一會(huì)才需要的圖片

[SGImageCache slowGetImageForURL:event.imageURL thenDo:nil];

通知緩存一張急需顯示的圖已經(jīng)不需要立刻顯示

[SGImageCache moveTaskToSlowQueueForURL:event.imageURL];

結(jié)論

通過專注于預(yù)取,隊(duì)列管理,從主線程移除耗時(shí)的任務(wù),并且依賴于UIImage內(nèi)置的內(nèi)存緩存,我們努力從一個(gè)簡單的軟件包中得到好的結(jié)果。

更多信息請(qǐng)查看IT技術(shù)專欄

更多信息請(qǐng)查看技術(shù)文章
易賢網(wǎng)手機(jī)網(wǎng)站地址:一款輕量級(jí)的 iOS 圖像緩存
由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢回復(fù)僅供參考,敬請(qǐng)考生以權(quán)威部門公布的正式信息和咨詢?yōu)闇?zhǔn)!

2025國考·省考課程試聽報(bào)名

  • 報(bào)班類型
  • 姓名
  • 手機(jī)號(hào)
  • 驗(yàn)證碼
關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡要咨詢 | 簡要咨詢須知 | 加入群交流 | 手機(jī)站點(diǎn) | 投訴建議
工業(yè)和信息化部備案號(hào):滇ICP備2023014141號(hào)-1 云南省教育廳備案號(hào):云教ICP備0901021 滇公網(wǎng)安備53010202001879號(hào) 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號(hào)
云南網(wǎng)警備案專用圖標(biāo)
聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關(guān)注公眾號(hào):hfpxwx
咨詢QQ:526150442(9:00—18:00)版權(quán)所有:易賢網(wǎng)
云南網(wǎng)警報(bào)警專用圖標(biāo)