劉辰基,周寶剛,郭淑琴
(1.浙江工業(yè)大學(xué)信息工程學(xué)院,浙江杭州310023;2.浙江科技學(xué)院,信息與電子工程學(xué)院,浙江杭州310023)
iOS是蘋果公司推出的手持設(shè)備操作系統(tǒng)。一方面,iOS設(shè)備提供給每個(gè)應(yīng)用軟件使用的內(nèi)存是有限的[1],而且內(nèi)存占用量過大的應(yīng)用軟件無法通過蘋果公司的審核。另一方面,擁有良好的用戶體驗(yàn)是應(yīng)用軟件大規(guī)模推廣的基礎(chǔ)。因此,在iOS應(yīng)用軟件的開發(fā)過程中,內(nèi)存控制和用戶體驗(yàn)是開發(fā)者需要時(shí)刻注意的兩個(gè)方面。UITableView是一種表格視圖,基本組成單元為UITableViewCell。無論是iOS設(shè)備自帶的通訊錄、備忘錄、提醒事項(xiàng)等應(yīng)用軟件,還是第三方的微信、微博、陌陌等社交軟件[2],都使用了這個(gè)視圖。然而,在平時(shí)的開發(fā)過程中,使用UITableView加載大批量數(shù)據(jù),經(jīng)常會(huì)遇到由于內(nèi)存占用量過大導(dǎo)致系統(tǒng)崩潰,以及用戶體驗(yàn)較差的問題,因此有必要對該系統(tǒng)進(jìn)行更深層面的優(yōu)化。
問題一:應(yīng)用軟件在使用UITableView加載大批量數(shù)據(jù)的時(shí)候出現(xiàn)內(nèi)存預(yù)警,繼而運(yùn)行崩潰。在數(shù)據(jù)量大的情況下,一些開發(fā)者直接將數(shù)據(jù)存放在內(nèi)存中,隨著不斷地從服務(wù)器加載數(shù)據(jù),占用的內(nèi)存空間越來越大。同時(shí),使用UITableView對數(shù)據(jù)進(jìn)行顯示時(shí),根據(jù)數(shù)據(jù)個(gè)數(shù)創(chuàng)建相應(yīng)數(shù)量的UITableView-Cell,也占用了很大的內(nèi)存空間。這些造成了應(yīng)用程序在使用UITableView加載大批量數(shù)據(jù)時(shí)內(nèi)存占用過大。
問題二:加載數(shù)據(jù)時(shí)讓用戶處于等待狀態(tài),而且等待時(shí)間過長,用戶體驗(yàn)較差。應(yīng)用軟件在使用UITableView加載大批量數(shù)據(jù)顯示的過程中,先從服務(wù)器加載數(shù)據(jù),加載成功后再使用UITableView進(jìn)行顯示。在從服務(wù)器加載數(shù)據(jù)的過程中,由于數(shù)據(jù)量大和網(wǎng)絡(luò)狀況的原因,耗費(fèi)了一定的時(shí)間。另一方面,視圖UITableView對大批量數(shù)據(jù)進(jìn)行顯示的過程,也耗費(fèi)了過長的時(shí)間。這些耗費(fèi)的時(shí)間一直讓用戶處于等待狀態(tài),造成了非常差的用戶體驗(yàn)。
首先,使用視圖UITableView對數(shù)據(jù)進(jìn)行顯示時(shí),開啟重用機(jī)制。重用機(jī)制是iOS系統(tǒng)中UITable-View自帶的一個(gè)機(jī)制,它在一定程度上能夠節(jié)約內(nèi)存,尤其是數(shù)據(jù)量大的時(shí)候[3]。
重用機(jī)制的核心是系統(tǒng)內(nèi)部創(chuàng)建了NSMutableArray類型的_visiableCells和NSMutableDictionary類型的_reusableTableCells。系統(tǒng)根據(jù)cell對應(yīng)的標(biāo)識(shí)符(Identifier)加載UITableViewCell。當(dāng)用戶滑動(dòng)屏幕時(shí),滑出屏幕的cell從可見變?yōu)椴豢梢?,此時(shí)系統(tǒng)將cell從_visiableCells取出,和它對應(yīng)的cell標(biāo)識(shí)符組成一組鍵值,再放入_reusableTableCells;劃入屏幕的cell從不可見變?yōu)榭梢姡藭r(shí)系統(tǒng)根據(jù)相應(yīng)的cell標(biāo)識(shí)符,從_reusableTableCells取出對應(yīng)的cell放入_visiableCells,如果_reusableTableCells中沒有,則創(chuàng)建一個(gè)新的cell,再放入_visiableCells。
開啟重用機(jī)制后,由于_visiableCells和_reusableTableCells中的cell構(gòu)成了一個(gè)循環(huán)重用,實(shí)際生成的cell總個(gè)數(shù)僅僅比屏幕中顯示cell最多數(shù)量的情況下多出一兩個(gè),這對內(nèi)存的節(jié)約是非常有效的。開啟重用機(jī)制的代碼如下:
static NSString*CellIdentifier=@ ”Cell”;
UITableViewCell*cell=[tableView dequeueResuableCellWithIdentifier:CellIdentifier];
if(cell==nil){
//創(chuàng)建新的cell
}
其次,對加載的數(shù)據(jù)進(jìn)行存儲(chǔ),并將相應(yīng)的內(nèi)存及時(shí)釋放。數(shù)據(jù)可以存放到SQLite數(shù)據(jù)庫。當(dāng)UITableView需要展示數(shù)據(jù)的時(shí)候,從相應(yīng)的存儲(chǔ)取出一條顯示一條,這樣可以一定程度的節(jié)約內(nèi)存。另一方面,將這些數(shù)據(jù)進(jìn)行存儲(chǔ),當(dāng)用戶需要再次讀取這些數(shù)據(jù)時(shí),就可以直接從存儲(chǔ)讀取,不必再次從服務(wù)器加載,有助于節(jié)省開銷。
首先,分批次和多線程加載數(shù)據(jù)。當(dāng)從服務(wù)器加載大批量數(shù)據(jù)到UITableView顯示的時(shí)候,由于數(shù)據(jù)量大,如果一次性地從服務(wù)器加載所有數(shù)據(jù),在網(wǎng)絡(luò)狀況不好的情況下,這一過程會(huì)耗費(fèi)很長時(shí)間。此時(shí),可以使用分批次加載,一次加載10條或者20條數(shù)據(jù)進(jìn)行存儲(chǔ)顯示,然后再進(jìn)行下次的加載。
在分批次加載數(shù)據(jù)時(shí),使用多線程[4]。如果使用單線程,系統(tǒng)在執(zhí)行加載數(shù)據(jù)請求時(shí),對于其它請求而言,主線程處于阻塞狀態(tài),數(shù)據(jù)加載完成后,主線程才會(huì)執(zhí)行其它請求,這導(dǎo)致用戶在數(shù)據(jù)加載過程中處于等待狀態(tài),用戶體驗(yàn)較差。開啟多線程能夠很好地解決這個(gè)問題。當(dāng)進(jìn)行查看下個(gè)批次數(shù)據(jù)操作的時(shí)候,開啟一個(gè)新的線程去加載數(shù)據(jù),加載結(jié)束后回到主線程調(diào)用[UITableView reloadData]對視圖進(jìn)行更新顯示。在這種情況下,主線程沒有阻塞,系統(tǒng)可以響應(yīng)用戶的其它請求,這是非常友好的。需要注意的是,此時(shí)更新視圖的操作需要放到主線程進(jìn)行,否則程序會(huì)運(yùn)行崩潰。
其次,由于每條數(shù)據(jù)的長度不一定相同,這使得對應(yīng)的UITableViewCell高度也不一定相同。因此,需要對cell的高度進(jìn)行動(dòng)態(tài)實(shí)現(xiàn)。cell的高度在UITableView Delegate相應(yīng)函數(shù)中進(jìn)行設(shè)置[5]。動(dòng)態(tài)高度要處理得當(dāng),否則會(huì)造成UITableView顯示過程耗費(fèi)時(shí)間過長,用戶體驗(yàn)較差。
一般情況下,動(dòng)態(tài)高度的計(jì)算方法有兩種。第一種方法的代碼如下:
-(CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath{
UITableViewCell*cell=[self tableView:tableView cellForRowAtIndexPath:indexPath];
return cell.frame.size.height;
}
第二種方法是在heightForRowAtIndexPath函數(shù)中根據(jù)數(shù)據(jù)動(dòng)態(tài)地計(jì)算各個(gè)cell的高度。
當(dāng)加載大批量數(shù)據(jù)的時(shí)候,選用第二種方法。因?yàn)榈谝环N方法執(zhí)行時(shí),系統(tǒng)重新調(diào)用了UITable-View Data Source的-(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath函數(shù),這造成了該函數(shù)的多次調(diào)用,耗費(fèi)時(shí)間。第二種方法是根據(jù)所要顯示的數(shù)據(jù)直接計(jì)算出相應(yīng)cell的高度,沒有額外的開銷。與第一種方法相比,在加載大數(shù)據(jù)的時(shí)候,第二種方法對性能影響較小。
同時(shí),通過計(jì)算得到的高度要進(jìn)行緩存處理。當(dāng)調(diào)用[UITableView reloadData]對視圖進(jìn)行刷新顯示時(shí),系統(tǒng)會(huì)多次調(diào)用heightForRowAtIndexPath函數(shù)。如果高度進(jìn)行了緩存處理,此時(shí)可以直接從緩存中讀取,而不需要重新計(jì)算,這在加載大批量數(shù)據(jù)的時(shí)候,能夠節(jié)約非??捎^的時(shí)間,可以有效地提升系統(tǒng)性能和用戶體驗(yàn)。
整個(gè)優(yōu)化方案的流程圖如圖1所示。
圖1 優(yōu)化方案流程圖
使用Xcode自帶的性能檢測工具Instruments,可以非常直觀地觀察應(yīng)用程序在運(yùn)行時(shí)的內(nèi)存使用情況[6]。整個(gè)測試過程中,應(yīng)用程序使用視圖UITableView從服務(wù)器加載320條數(shù)據(jù)進(jìn)行顯示。測試過程中其它部分都保持相同,唯一的區(qū)別是一種沒有進(jìn)行優(yōu)化,另一種按照文中所給的優(yōu)化方案流程進(jìn)行了處理。最后測試結(jié)果顯示,未進(jìn)行優(yōu)化時(shí),內(nèi)存使用情況如圖2所示。按照優(yōu)化方案進(jìn)行處理后,內(nèi)存使用情況如圖3所示。
圖2 優(yōu)化前內(nèi)存使用量
圖3 優(yōu)化后內(nèi)存使用量
圖3中,All Allocations是程序運(yùn)行時(shí)所有分配的對象。Live Bytes表示當(dāng)前存活對象在內(nèi)存中所占用的字節(jié)數(shù)。Overall Bytes表示從程序開始運(yùn)行,對象使用的總字節(jié)數(shù),這里的對象包括已經(jīng)銷毀的和仍然存活的。程序所占用的內(nèi)存為All Allocations類中,Live Bytes一欄所顯示的數(shù)字。對比圖2、3,可以清楚地看到,按照優(yōu)化方案進(jìn)行優(yōu)化后,內(nèi)存占用量減少。同時(shí),按照優(yōu)化方案處理后,用戶等待時(shí)間減少,顯示速度加快,用戶體驗(yàn)得到提升。
本文針對iOS開發(fā)過程中,使用視圖UITableView加載大批量數(shù)據(jù)時(shí)出現(xiàn)的內(nèi)存占用量過大和用戶體驗(yàn)差問題,分別從數(shù)據(jù)的加載、存儲(chǔ)、展示3個(gè)方面出發(fā),介紹了相應(yīng)的優(yōu)化方法。方法經(jīng)過實(shí)際項(xiàng)目的測試,成功使應(yīng)用程序在系統(tǒng)性能和用戶體驗(yàn)方面得到了提升。在實(shí)際開發(fā)過程中,系統(tǒng)性能和用戶體驗(yàn)的提升是沒有止境的,這需要開發(fā)者根據(jù)實(shí)際情況進(jìn)行相應(yīng)的優(yōu)化,從而開發(fā)出性能更加優(yōu)良的應(yīng)用程序。
[1]劉樂廷,李敬兆.IOS內(nèi)存開發(fā)管理機(jī)制的研究[J].計(jì)算機(jī)與現(xiàn)代化,2013,29(3):196-199.
[2]李慧慧.3G時(shí)代手機(jī)傳播的特性及模式分析[D].北京:北京郵電大學(xué),2012:11-13.
[3]Dave Mark,Jeff LaMarche.漆振,解巧云,孫文磊,等譯.iPhone開發(fā)基礎(chǔ)教程[M].北京:人民郵電出版社,2009:50.
[4]XMobileApp.iPhone創(chuàng)意開發(fā)入門與實(shí)戰(zhàn)[M].北京:人民郵電出版社,2010:395.
[5]Erica Sadun.漆振,解巧云,郎亞妹,等譯.iPhone開發(fā)秘籍[M].北京:人民郵電出版社,2009:156.
[6]劉銘,朱舸,王佳.iPhone程序開發(fā)基礎(chǔ)教程[M].北京:電子工業(yè)出版社,2011:11.