(轉)iOS中UITableView性能優化,超實用

1. Cell重用

1.1>數據源方法優化

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;

在可見的頁面會重復繪制頁面,每次刷新顯示都會去創建新的Cell,非常耗費性能。

解決方案:首先創建一個靜態變量reuseID(代理方法返回Cell會調用很多次,防止重復創建,static保證只會被創建一次,提高性能),然后,從緩存池中取相應identifier的Cell并更新數據,如果沒有,才開始alloc新的Cell,并用identifier標識Cell。每個Cell都會注冊一個identifier(重用標識符)放入緩存池,當需要調用的時候就直接從緩存池里找對應的id,當不需要時就放入緩存池等待調用。(移出屏幕的Cell才會放入緩存池中,并不會被release)所以在數據源方法中做出如下優化:

static NSString *reuseID = “reuseCellID”;```

```// 緩存池中取已經創建的cell
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseID];```

 ###### 1.2>緩存池的實現

   當Cell要alloc時,UITableView會在堆中開辟一段內存以供Cell緩存之用。Cell的重用通過identifier標識不同類型的Cell,由此可以推斷出,緩存池外層可能是一個可變字典,通過key來取出內部的Cell,而緩存池為存儲不同高度、不同類型(包含圖片、Label等)的Cell,可以推斷出緩存池的字典內部可能是一個可變數組,用來存放不同類型的Cell,緩存池中只會保存已經被移出屏幕的不同類型的Cell。

 ######1.3>緩存池獲取可重用Cell兩個方法的區別
```-(nullable __kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier; ```
這個方法會查詢可重用Cell,如果注冊了原型Cell,能夠查詢到,否則,返回nil;而且需要判斷if(cell == nil),才會創建Cell,不推薦

```-(__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0);```
使用這個方法之前,必須通過xib(storyboard)或是Class(純代碼)注冊可重用Cell,而且這個方法一定會返回一個Cell

######注冊Cell
```- (void)registerNib:(nullable UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);```
```- (void)registerClass:(nullable Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);```
好處:如果緩沖區 Cell 不存在,會使用原型 Cell 實例化一個新的 Cell,不需要再判斷,同時代碼結構更清晰。

##2. 定義一種(盡量少)類型的Cell及善用hidden隱藏(顯示)subviews
###### 2.1>一種類型的Cell

   分析Cell結構,盡可能的將 相同內容的抽取到一種樣式Cell中,前面已經提到了Cell的重用機制,這樣就能保證UITbaleView要顯示多少內容,真正創建出的Cell可能只比屏幕顯示的Cell多一點。雖然Cell的’體積’可能會大點,但是因為Cell的數量不會很多,完全可以接受的。好處:

a.減少代碼量,減少Nib文件的數量,統一一個Nib文件定義Cell,容易修改、維護

b.基于Cell的重用,真正運行時鋪滿屏幕所需的Cell數量大致是固定的,設為N個。所以如果如果只有一種Cell,那就是只有N個Cell的實例;但是如果有M種Cell,那么運行時最多可能會是“M x N = MN”個Cell的實例,雖然可能并不會占用太多內存,但是能少點不是更好嗎。

  ###### 2.2>善用hidden隱藏(顯示)subviews

   只定義一種Cell,那該如何顯示不同類型的內容呢?答案就是,把所有不同類型的view都定義好,放在cell里面,通過hidden顯示、隱藏,來顯示不同類型的內容。畢竟,在用戶快速滑動中,只是單純的顯示、隱藏subview比實時創建要快得多。

##3. 提前計算并緩存Cell的高度
   在[iOS](http://lib.csdn.net/base/1)中,不設UITableViewCell的預估行高的情況下,會優先調用”tableView:heightForRowAtIndexPath:”方法,獲取每個Cell的即將顯示的高度,從而確定UITableView的布局,實際就是要獲取contentSize(UITableView繼承自UIScrollView,只有獲取滾動區域,才能實現滾動),然后才調用”tableView:cellForRowAtIndexPath”,獲取每個Cell,進行賦值。如果項目中模塊有10000個Cell需要顯示,可想而知…

   解決方案:我個人認為,可以創建一個frame模型,提前計算每個Cell的高度。參考其中一篇博客的時候,在解決這個問題的時候,可以將計算Cell的高度放入數據模型,但這與MVC設計模式可能稍微有點沖突,這個時候我就想到MVVM這種設計模式,這個時候才能稍微有點MVVM這種設計模式的優點(其實還是很不理解的),可以講計算Cell高度放入ViewModel(視圖模型)中,讓Model(數據模型)只負責處理數據。

   在上面的基礎上,還可以繼續進行優化,提前創建真正顯示的、需要加工的數據并緩存。不過這方面優化我好像之前沒有接觸。大家可以去看看這篇博客,其實本篇性能優化也借鑒了好多這篇文章,這是土土大神的博客地址,大家可以進去看看,好多[iOS](http://lib.csdn.net/base/ios)開發的知識,當然也有我參照的這篇博客。[http://tutuge.me](http://tutuge.me/)

##4.異步繪制(自定義Cell繪制)
 遇到比較復雜的界面的時候,如復雜點的圖文混排,上面的那種優化行高的方式可能就不能滿足要求了,當然了,由于我的開發經驗尚短,說實話,還沒遇到要將自定義的Cell重新繪制。至于這方面,大家可以參考這篇博客,絕對是開發經驗十足的大神,分享足夠多的UITableView方面的性能優化,好多借鑒自這里,我都不好意思了。[http://www.cocoachina.com/ios/20150602/11968.html](http://www.cocoachina.com/ios/20150602/11968.html)

##5.滑動時,按需加載
開發的過程中,自定義Cell的種類千奇百怪,但Cell本來就是用來顯示數據的,不說100%帶有圖片,也差不多,這個時候就要考慮,下滑的過程中可能會有點卡頓,尤其網絡不好的時候,異步加載圖片是個程序員都會想到,但是如果給每個循環對象都加上異步加載,開啟的線程太多,一樣會卡頓,我記得好像線程條數一般3-5條,最多也就6條吧。這個時候利用UIScrollViewDelegate兩個代理方法就能很好地解決這個問題。

```- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate```
```- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView```
思想就是識別UITableView禁止或者減速滑動結束的時候,進行異步加載圖片,快滑動過程中,只加載目標范圍內的Cell,這樣按需加載,極大的提高流暢度。而SDWebImage可以實現異步加載,與這條性能配合就完美了,尤其是大量圖片展示的時候。而且也不用擔心圖片緩存會造成內存警告的問題。

//獲取可見部分的Cell
NSArray *visiblePaths = [self.tableView indexPathsForVisibleRows];
for (NSIndexPath *indexPath in visiblePaths)
{
//獲取的dataSource里面的對象,并且判斷加載完成的不需要再次異步加載
<code>
}```

記得在記得在“tableView:cellForRowAtIndexPath:”方法中加入判斷:

// tableView 停止滑動的時候異步加載圖片
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

         if (self.tableView.dragging == NO && self.tableView.decelerating == NO)
            {
               //開始異步加載圖片
                <code>
            }```


##6.緩存View

 當Cell中的部分View是非常獨立的,并且不便于重用的,而且“體積”非常小,在內存可控的前提下,我們完全可以將這些view緩存起來。當然也是緩存在模型中。

##7.避免大量的圖片縮放、顏色漸變等,盡量顯示“大小剛好合適的圖片資源”

##8.避免同步的從網絡、文件獲取數據,Cell內實現的內容來自web,使用異步加載,緩存請求結果

##9.渲染
 ######9.1>減少subviews的個數和層級
子控件的層級越深,渲染到屏幕上所需要的計算量就越大;如多用drawRect繪制元素,替代用view顯示

  ###### 9.2>少用subviews的透明圖層
對于不透明的View,設置opaque為YES,這樣在繪制該View時,就不需要考慮被View覆蓋的其他內容(盡量設置Cell的view為opaque,避免GPU對Cell下面的內容也進行繪制)

 ######9.3>避免CALayer特效(shadowPath)
  給Cell中View加陰影會引起性能問題,如下面代碼會導致滾動時有明顯的卡頓:
```view.layer.shadowColor = color.CGColor;
view.layer.shadowOffset = offset;
view.layer.shadowOpacity = 1;
view.layer.shadowRadius = radius;```


##總結:UITableView的優化主要從三個方面入手:

#####1.提前計算并緩存好高度(布局),因為heightForRowAtIndexPath:是調用最頻繁的方法;(這個是開發中肯定會要優化的,不可能一個app就幾個Cell吧)


#####2.滑動時按需加載,防止卡頓,這個我也認為是很有必要做的性能優化,配合SDWebImage

#####3.異步繪制,遇到復雜界面,遇到性能瓶頸時,可能就是突破口(如題,遇到復雜的界面,可以從這入手)

#####4.緩存一切可以緩存的,這個在開發的時候,往往是性能優化最多的方向
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容