最近看到有些面試題中會(huì)問(wèn)到TableView的優(yōu)化境蔼,特定花了幾天時(shí)間研究了一下各種優(yōu)化技巧灶平,主要就分為幾個(gè)主要方向:
- 從重用Cell的方面去優(yōu)化
- 從圖層屬性箍土,圓角/陰影等方面去優(yōu)化
- 從UIView的繪制方面去優(yōu)化
- 從預(yù)計(jì)算和緩存高度逢享,按需加載的方面去優(yōu)化
從一個(gè)初學(xué)者寫(xiě)的卡到爆的TableView例子說(shuō)起(引用自這里)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ContacterTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ContacterTableCell"];
if (!cell) {
cell = (ContacterTableCell *)[[[NSBundle mainBundle] loadNibNamed:@"ContacterTableCell" owner:self options:nil] lastObject];
}
NSDictionary *dict = self.dataList[indexPath.row];
[cell setContentInfo:dict];
return cell;
}
如果沒(méi)有在xib中設(shè)置重用的標(biāo)識(shí),上面的cell就不會(huì)發(fā)生重用吴藻,只要TableView發(fā)生滾動(dòng)瞒爬,cellForRowAtIndexPath就會(huì)被調(diào)用,每一次cellForRowAtIndexPath被調(diào)用都會(huì)從NSBundle中獲取View的示例再賦給cell沟堡。眾所周知從NSBundle中讀取數(shù)據(jù)是非常的慢的侧但,所以這樣寫(xiě)出的代碼必定會(huì)卡到爆(我當(dāng)初剛學(xué)iOS的時(shí)候就是這樣寫(xiě)的(-_-))。
## 既然卡到爆就重用啰
首先注冊(cè)cell
table = UITableView(frame: self.view.bounds, style: UITableViewStyle.Plain)
table?.registerClass(UITableViewCell.self, forCellReuseIdentifier: "mainViewControllerCellId")
然后重用
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("mainViewControllerCellId")
// config cell data
cell?.textLabel?.text = titles[indexPath.row]
cell?.selectionStyle = .None
return cell!
}
可以看到航罗,使用了基本的重用機(jī)制后,整個(gè)TableView就變得流暢起來(lái)了粥血,而且按照這種做法燕侠,cell的生產(chǎn)和使用就分開(kāi)了立莉,cellForRowAtIndexPath就只專注于設(shè)置cell的數(shù)據(jù),不關(guān)心cell的初始化七问。一般的TableView界面采用這種寫(xiě)法都能獲得不錯(cuò)的性能,而且有一定經(jīng)驗(yàn)的iOS老手都是這樣寫(xiě)的械巡。
## 萬(wàn)一Cell中有圓角蒙版陰影透明這些圖層屬性呢
在UIView中刹淌,透明效果的渲染讥耗,圓角的渲染有勾,蒙版的渲染,陰影的渲染會(huì)消耗很大部分的性能古程。在靜態(tài)的View之中,這部分只消耗一次挣磨,不會(huì)造成太大的影響雇逞,但是在想U(xiǎn)ITableViewCell這些重用的動(dòng)態(tài)的View中,每一次的重用都會(huì)消耗一次塘砸,所以總的性能消耗會(huì)很大。當(dāng)然晤锥,要想在cell中使用這些屬性廊宪,還是有優(yōu)化的技巧來(lái)減少性能的消耗的。
#### 首先女轿,盡量減少透明View箭启。
UIView有一個(gè)屬性```opaque ```谈喳,當(dāng)不需要透明時(shí)可以將其設(shè)置為true册烈,這樣可以減少性能消耗。
#### 其次婿禽,減少離屏渲染
通常圖層的以下屬性將會(huì)觸發(fā)離屏渲染:
* 陰影(UIView.layer.shadowOffset/shadowRadius/...)
* 圓角(當(dāng) UIView.layer.cornerRadius 和 UIView.layer.maskToBounds 一起使用時(shí))
* 圖層蒙板
對(duì)于以上這些,要減少離屏渲染比較好的方法是使用shadowPath來(lái)設(shè)置陰影扭倾,使用裁剪過(guò)的圖片代替圓角而不是使用cornerRadius淀零,圖層模板能不用就不用膛壹。
#### 最后驾中,合理使用光柵化
當(dāng)然,除了第二點(diǎn)減少離屏渲染外肩民,還有一點(diǎn)就是要合理使用光柵化。使用光柵化能夠?qū)⒛軌蛴行岣咝阅芰捶剑琜把layer的shouldRasterize設(shè)為YES后,CALayer會(huì)被光柵化為bitmap祟蚀,layer的陰影等效果也會(huì)被保存到bitmap中作為緩存工窍。在使用了shadow或cornerRadius等效果時(shí),緩存使性能得到提升](http://zhijiang.me/2015/08/03/%E5%BD%B1%E5%93%8D%E5%9B%BE%E5%BD%A2%E6%80%A7%E8%83%BD%E7%9A%84%E5%9B%A0%E7%B4%A0%E5%92%8C%E4%BD%BF%E7%94%A8Instrument%E8%BF%9B%E8%A1%8C%E6%A3%80%E6%B5%8B/)患雏。
但是,使用光柵化要注意幾點(diǎn):
- 更新已經(jīng)光柵化的CALayer會(huì)造成離屏渲染 (這最重要)
- 被光柵化的bitmap如果超過(guò)100ms沒(méi)有被使用則會(huì)被移除
- 系統(tǒng)限制緩存的大小為2.5 x screen size
對(duì)于經(jīng)常改動(dòng)的罢维,不易采用光柵化,否則會(huì)增加離屏渲染肺孵,增加性能消耗攻人。對(duì)于相對(duì)靜態(tài)的入客,建議采用光柵化。特別是界面比較復(fù)雜给赞,動(dòng)畫(huà)比較復(fù)雜的初婆,都建議使用光柵化蓬坡。
## 糟糕我遇到了很復(fù)雜的Cell
由于繪制渲染復(fù)雜View的過(guò)程是非常耗時(shí)的猿棉,有時(shí)真的不排除遇到很復(fù)雜CellView的情況。由于本人對(duì)Core Graphics不太熟練屑咳,而且最近有點(diǎn)小忙,所以就不提供代碼兆龙,只提供一些優(yōu)化思路杖爽。
核心優(yōu)化就兩點(diǎn):
1. 使用Core Graphics進(jìn)行重繪紫皇,更進(jìn)一步就是將所有subViews繪制成一張圖片慰安,通過(guò)消除View樹(shù)的層級(jí)來(lái)提高效率。通俗來(lái)說(shuō)就是所謂的扁平化化焕。。铃剔。
通過(guò)重繪來(lái)壓縮View樹(shù)層級(jí),能大大提高View的繪制效率键兜,特別對(duì)于一些復(fù)雜界面效果特別明顯凤类。當(dāng)然缺點(diǎn)就是使用重繪比較復(fù)雜,開(kāi)發(fā)一些相對(duì)簡(jiǎn)單的界面可能會(huì)影響開(kāi)發(fā)效率踱蠢。
2.異步繪制渲染。
這個(gè)就是將View的繪制渲染放到后臺(tái)線程中棋电,完成后再通知主線程更新UI。這個(gè)思路跟Facebook的一個(gè)非常出名的開(kāi)源框架[AsyncDisplayKit](https://github.com/facebook/AsyncDisplayKit)類似苇侵。
## 我想我的TableView更動(dòng)人
如何更進(jìn)一步優(yōu)化TableView的體驗(yàn)赶盔?答案就是就兩點(diǎn):
- cell高度緩存和預(yù)計(jì)算
- 按需加載
對(duì)于第一點(diǎn)cell高度緩存和預(yù)計(jì)算榆浓,簡(jiǎn)單來(lái)說(shuō)就是計(jì)算每一個(gè)cell的高度并緩存起來(lái)于未,避免重復(fù)計(jì)算陡鹃。這里有個(gè)更合理的方法是[在用戶不滑動(dòng)TableView時(shí)烘浦,通過(guò)監(jiān)聽(tīng)runloop來(lái)啟動(dòng)后臺(tái)cell高度計(jì)算和緩存](http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/)。這樣做的好處時(shí)將用戶交互和耗時(shí)計(jì)算任務(wù)進(jìn)行合理的分時(shí)闷叉,既不影響用戶交互,也不浪費(fèi)CPU計(jì)算性能脊阴。
對(duì)于第二點(diǎn)蚯瞧,用戶滾動(dòng)TableView的時(shí)候,希望能盡快看到當(dāng)前屏幕顯示的內(nèi)容品擎,而不關(guān)心其他還沒(méi)有滾動(dòng)到當(dāng)前屏幕的內(nèi)容埋合。所以萄传,一個(gè)比較有效的優(yōu)化思路是優(yōu)先繪制加載當(dāng)前的內(nèi)容甚颂,暫時(shí)忽略其他內(nèi)容。根據(jù)這個(gè)思路秀菱,可以使用UIScrollView的協(xié)議方法振诬,監(jiān)聽(tīng)滾動(dòng)的offset,然后當(dāng)用戶暫停滾動(dòng)時(shí)的屏幕贷揽,就是需要優(yōu)先加載的屏幕內(nèi)容,這時(shí)候可以再去加載當(dāng)前內(nèi)容梦碗,而不是一開(kāi)始按照滾動(dòng)順序全部加載。這樣的話就不用特地設(shè)置一個(gè)網(wǎng)絡(luò)請(qǐng)求的隊(duì)列來(lái)管理網(wǎng)絡(luò)請(qǐng)求洪规,而且發(fā)出去了的請(qǐng)求也是cancel不了的印屁。
## 總結(jié)
感覺(jué)對(duì)TableView的優(yōu)化有了點(diǎn)思路斩例,但是優(yōu)化方法太多以至于還沒(méi)消化過(guò)來(lái)雄人,但是大的優(yōu)化方向也是很清晰的:
- 能重用就重用
- 盡量減少view層級(jí)
- 使用異步繪制
- 使用緩存
- 按需加載
- 對(duì)于某些layer屬性盡量采用替代方案
總之,這幾天對(duì)這個(gè)專題的研究础钠,收獲很多,特定分享出來(lái)叉谜,寫(xiě)的不好旗吁,還望指教停局。
## 參考
[優(yōu)化UITableViewCell高度計(jì)算的那些事](http://blog.sunnyxx.com/2015/05/17/cell-height-calculation/)
[[iOS 程序性能優(yōu)化](http://www.samirchen.com/ios-performance-optimization)](http://www.samirchen.com/ios-performance-optimization/)
[UITableView優(yōu)化技巧](http://longxdragon.github.io/2015/05/26/UITableView%E4%BC%98%E5%8C%96%E6%8A%80%E5%B7%A7/)
[如何加強(qiáng) iOS 里的列表滾動(dòng)時(shí)的順暢感很钓?](http://www.zhihu.com/question/20382396)
[影響圖形性能的因素和使用Instrument進(jìn)行檢測(cè)](http://zhijiang.me/2015/08/03/%E5%BD%B1%E5%93%8D%E5%9B%BE%E5%BD%A2%E6%80%A7%E8%83%BD%E7%9A%84%E5%9B%A0%E7%B4%A0%E5%92%8C%E4%BD%BF%E7%94%A8Instrument%E8%BF%9B%E8%A1%8C%E6%A3%80%E6%B5%8B/)