讀后感系列之TableView滾動(dòng)性能優(yōu)化

本文為純手打,內(nèi)容取自南峰子老師的博客:http://southpeak.github.io/

里面有些地方?jīng)]有實(shí)踐,也不是很理解,大家共同學(xué)習(xí)旱函。



內(nèi)建方法:

首先是重用cell/header/footer的單個(gè)實(shí)例,幾遍是我們需要顯示多個(gè).這是優(yōu)化UIScrollerView最明顯的方式,為了正確的使用它,你應(yīng)該只有cell/header/footer類,一次性初始化他們,并返回給UITableView.

蘋果開發(fā)文檔里有重用cell的流程.

重要的事情是:在UITableView的dataSource中實(shí)現(xiàn)的tableView:cellForRowAtIndexPath:方法,需要為每個(gè)cell調(diào)用一次,他應(yīng)該快速執(zhí)行.所以需要盡可能快地返回重用cell實(shí)例.不要在這里進(jìn)行數(shù)據(jù)綁定,因?yàn)槟壳霸谄聊簧线€沒(méi)有cell.為了執(zhí)行數(shù)據(jù)綁定可以在UITableView的delegate方法tableView:willDisplayCell:forRowAtIndexPath:中進(jìn)行.這個(gè)方法在顯示之前會(huì)被調(diào)用.

這個(gè)方法對(duì)于cell定高的UITableView來(lái)說(shuō)沒(méi)有意義,但如果由于某些原因需要?jiǎng)討B(tài)高度的cell的話,這個(gè)方法可以很容易地讓滑動(dòng)更流暢.

正如我們所知,UITableView是UIScrollerView的子類,而UIScrollerView的作用是讓用戶可以與比屏幕尺寸更大的區(qū)域交互.任何UIScrollerView的實(shí)例都是用諸如contentSizew、contentOffset和其他許多屬性來(lái)將正確的區(qū)域顯示給用戶.

但是UITableView得問(wèn)他在哪描滔?正如所解釋的一樣棒妨,UITableView不會(huì)同時(shí)維護(hù)所有cell的實(shí)例。相反他只需要維護(hù)顯示給用戶的那些cell含长。

那么券腔,UITableView是如何知道她的contentSize呢?他是通過(guò)計(jì)算所有cell的高度之和來(lái)計(jì)算contentSize的值拘泞。

UITableView的delegate方法tableView:heightForRowAtIndexPath:會(huì)為每個(gè)cell調(diào)用一次纷纫,所以你應(yīng)該非常快地返回高度值陪腌。

很多人會(huì)犯一個(gè)錯(cuò)誤辱魁,他們會(huì)在布局初始化cell實(shí)例并綁定數(shù)據(jù)后獲取他們的高度烟瞧。如果你想優(yōu)化滑動(dòng)的性能,就不應(yīng)該以這種方式來(lái)計(jì)算cell的高度染簇,因?yàn)檫@事難以置信的低效参滴,iOS設(shè)備標(biāo)準(zhǔn)的60FPS將會(huì)降低到15-20FPS,滑動(dòng)會(huì)變得很慢锻弓。

如果我們沒(méi)有一個(gè)cell的實(shí)例砾赔,那如何計(jì)算他的高度呢?

它使用類方法青灼,并基于傳入的寬度及顯示的數(shù)據(jù)來(lái)計(jì)算高度值

可以用以下方式來(lái)使用上面這個(gè)方法返回高度值給UITableView:

從iOS8 開始暴心,我們可以在UITableView的delegate中使用自動(dòng)高度計(jì)算,而不需要實(shí)現(xiàn)上面提到的方法杂拨。為了實(shí)現(xiàn)這一功能专普,你可能會(huì)使用AutoLayout,并將rowHeight變量設(shè)置為UITableViewAutomaticDimension扳躬〈嗨撸可以在StackOverflow中找到更多詳細(xì)信息甚亭。

如果你想讓你的App在所有設(shè)備上都能平滑的滾動(dòng)贷币,你會(huì)發(fā)現(xiàn)這種方法難以置信的慢,你使用的子視圖越多亏狰,AutoLayout的效率越低役纹,原因是隱藏在底層的命名為“Cassowary”的約束求解系統(tǒng)。如果布局中子視圖越多暇唾,那么需要求解的約束也越多促脉,進(jìn)而返回cell給UITableView所花的時(shí)間也越多。

使用內(nèi)建方法優(yōu)化UITableView的正確方法是:

重用cell實(shí)例:對(duì)于特殊類型的cell策州,你應(yīng)該只有一個(gè)實(shí)例瘸味,而沒(méi)有更多。

不要在cellForRowAtIndexPath:方法中綁定數(shù)據(jù)够挂,因?yàn)樵诖藭r(shí)cell還沒(méi)有顯示旁仿。可以使用UITableView的delegate中的tableView:willDisplayCell:forRowAtIndexPath:方法孽糖。

快速計(jì)算cell高度枯冈。


更深一步


上面提到的這些點(diǎn)不足以實(shí)現(xiàn)真正的平滑滾動(dòng),特別是當(dāng)你需要實(shí)現(xiàn)一些復(fù)雜的cell(如有大量的簡(jiǎn)便办悟、視圖尘奏、交互元素、一些修飾元素等等)時(shí)病蛉,這變得尤其明顯炫加。

這種情況下瑰煎,UITableView很容易變得緩慢,即便是做了上面所有的事情琢感。UITableViewCell中的視圖越多丢间,滑動(dòng)時(shí)FPS越低。但在使用了手動(dòng)布局優(yōu)化了高度計(jì)算后驹针,問(wèn)題就不在布局了烘挫,而在渲染了。

讓我們把關(guān)注點(diǎn)放在UIView的opaque上屬性上柬甥,文檔中說(shuō)它用于輔助繪圖系統(tǒng)定義UIView是否透明饮六,如果不透明,則繪圖系統(tǒng)在渲染視圖時(shí)可以做一些優(yōu)化苛蒲,以提高性能卤橄。

我們需要性能,或者不是臂外?用戶可能快速地滑動(dòng)table窟扑,如使用scrollsToTop特性,但他們可能沒(méi)有最新的iPhone漏健,所以cell必須快速的被渲染嚎货。比通常的視圖更快。

渲染最慢的操作之一是混合(blending)蔫浆≈呈簦混合操作由GPU來(lái)執(zhí)行,因?yàn)檫@個(gè)硬件就是用來(lái)做混合操作的(當(dāng)然不只是混合)瓦盛。

你可能已經(jīng)猜到提高性能的方法是減少混合操作的次數(shù)洗显,在此之前,我們需要找到它原环。

在模擬器上運(yùn)行APP挠唆,在模擬器的菜單中選擇‘Debug’,然后選中‘Color Blended Layers’嘱吗。然后模擬器就會(huì)將全部區(qū)域顯示為兩種顏色:綠色和紅色玄组。(綠色沒(méi)有混合,紅色區(qū)域表示有混合操作)每種情況都應(yīng)該仔細(xì)研究柜与,不同的情況需要使用不同的方法來(lái)避免混合巧勤。一般做法是設(shè)置backgroundColor來(lái)實(shí)現(xiàn)非透明。

但有時(shí)候可能更復(fù)雜弄匕。有一個(gè)漸變颅悉,但是沒(méi)有混合。


如果想要使用CAGradientLayer來(lái)實(shí)現(xiàn)這個(gè)效果迁匠,你將會(huì)很失望:在iPhone6中FPS將會(huì)降到25-30剩瓶,快色滑動(dòng)變得不可能驹溃。

這確實(shí)發(fā)生了,因?yàn)槲覀兓旌狭藘蓚€(gè)不同層的內(nèi)容:UILabel的CATextLayer和我們的CAGradientLayer延曙。

當(dāng)設(shè)備需要執(zhí)行很多混合操作時(shí)豌鹤,問(wèn)題就出現(xiàn)了:GPU是滿載的,但CPU卻保持低負(fù)載枝缔,而顯得沒(méi)有太大用處布疙。

所以需要怎么做呢?事實(shí)上愿卸,解決的方案是:使用CPU來(lái)渲染灵临!這將不會(huì)加載GPU,這樣就無(wú)法執(zhí)行混合操作趴荸,例如儒溉,在執(zhí)行動(dòng)畫的CALayer上。

我們可以在UIView的drawRect:方法中使用CoreGraphics操作來(lái)執(zhí)行CPU渲染发钝,


通過(guò)這種方式顿涣,你會(huì)撤銷在一些UIView上(在任何情況下,他們都是不必要的)的所有緩存優(yōu)化操作酝豪。但是涛碑,這種方法禁用了一些混合操作,卸載GPU寓调,從而使UITableView更順暢锌唾。

但是記壮搿:這提高了渲染性能夺英,不是因?yàn)镃PU比GPU更快,他可以讓我們通過(guò)為讓CPU來(lái)執(zhí)行某些渲染任務(wù)滋捶,從而卸載GPU痛悯,因?yàn)樵诤芏嗲闆r下,CPU可能不是100%負(fù)載的重窟。優(yōu)化混合操作的關(guān)鍵點(diǎn)是在平衡CPU和GPU的負(fù)載载萌。(這個(gè)CoreGraphics好難理解感覺(jué)!)

優(yōu)化UITableView中繪制數(shù)據(jù)操作的小結(jié):

減少iOS執(zhí)行無(wú)用混合的區(qū)域:不要使用透明背景巡扇,使用模擬器或者Instruments來(lái)確認(rèn)這一點(diǎn)扭仁,如果可以盡量使用沒(méi)有混合的漸變。

優(yōu)化代碼厅翔,以平衡CPU和GPU的負(fù)載乖坠。你需要清楚的知道那部分渲染需要使用GPU,那部分可以使用CPU刀闷,以此保持平衡熊泵。

為特殊的cell類型編寫特殊的代碼仰迁。


像素獲取


自從有了Retina屏后,在Cocoa Touch環(huán)境下顽分,我們就可以用屏幕點(diǎn)來(lái)取代像素了徐许,同時(shí)屏幕點(diǎn)可以是浮點(diǎn)值。

現(xiàn)實(shí)生活中它可能是浮點(diǎn)值卒蘸,例如雌隅,線段可能起始于x為0.25的地方。這時(shí)候缸沃,iOS將執(zhí)行子像素渲染澄步。這一技術(shù)在應(yīng)用于特定類型的內(nèi)容是很有意義。但當(dāng)我們繪制平滑直線時(shí)則沒(méi)有必要和泌。

如果所有的平滑線段都使用子像素渲染技術(shù)來(lái)渲染村缸,那會(huì)讓你iOS執(zhí)行一些不必要的任務(wù),從而降低FPS武氓。

什么情況下會(huì)出現(xiàn)這種不必要的子像素抗鋸齒操作呢梯皿?最常發(fā)生的情況是通過(guò)代碼計(jì)算而變成浮點(diǎn)值的視圖坐標(biāo),或者是一些不正常的圖片資源县恕,這些圖片的大小不是對(duì)起到屏幕的物理像素上的(例如东羹,你有一張?jiān)赗etina顯示屏上的大小為60*61的圖片,而不是60*60的)忠烛。

在前面我們講到属提,要解決問(wèn)題,首先需要找到問(wèn)題在哪美尸。在模擬器上運(yùn)行程序冤议,在“Debug”菜單中選中“Color Misaligned Image”。

這一次有兩種高亮區(qū)域:品紅色區(qū)域會(huì)執(zhí)行子像素渲染师坎,而黃色區(qū)域是圖片大小沒(méi)有對(duì)齊的情況恕酸。

通常,為了解決這個(gè)問(wèn)題胯陋,你只要簡(jiǎn)單地使用ceilf蕊温,floorf和CGRectIntegral方法來(lái)對(duì)坐標(biāo)做四舍五入處理。就是這樣遏乔!

通過(guò)上面的討論义矛,我想建議你以下幾點(diǎn):

對(duì)所有像素相關(guān)的數(shù)據(jù)做四舍五入處理,包括點(diǎn)坐標(biāo)盟萨,UIView的高度和寬度凉翻。

跟蹤你的圖像資源:圖片必須是像素完美的,否則在Retina屏幕上渲染時(shí)鸯旁,它會(huì)做不必要的抗鋸齒處理噪矛。

定期復(fù)查你的代碼量蕊,因?yàn)檫@種情況可能會(huì)經(jīng)常出現(xiàn)。


異步UI

每個(gè)中等以上規(guī)模的應(yīng)用都可能會(huì)使用帶有媒體內(nèi)容的cell:文本艇挨、圖片残炮、動(dòng)畫,甚至還有視頻缩滨。而所有這些都可能帶有裝飾元素:圓角頭像势就、帶‘#’號(hào)的文本、用戶名等脉漏。

我們已經(jīng)多次提及盡可能快地返回cell的需求苞冯,而在這里有一些麻煩:clipsToBounds很慢,圖片需要從網(wǎng)絡(luò)加載侧巨,需要在字符串中定位#號(hào)舅锄,和許多其他的問(wèn)題。

優(yōu)化的目標(biāo)是很明確的:如果在主線程中執(zhí)行這些操作司忱,則會(huì)讓你不能很快地返回cell皇忿。

在后臺(tái)加載圖片,在相同的地方處理圓角坦仍,然后將處理后的圖片制定給UIimageView鳍烁。

立刻顯示文本,但在后臺(tái)定位#號(hào)繁扎,然后使用屬性字符串來(lái)刷新顯示幔荒。

在你的cell中,需要具體情況具體分析梳玫,但主要的思想是在后臺(tái)執(zhí)行大的操作爹梁。這可能不只是網(wǎng)絡(luò)代碼,你需要使用Instruments來(lái)找到他們汽纠。

記孜兰:需要盡快返回cell

有時(shí)候傀履,上面的所有技術(shù)可能都幫不上忙虱朵。如GPU仍然不能使用時(shí)(iPhone4+iOS7),cell中有很多內(nèi)容時(shí)钓账,需要CALayer的支持以實(shí)現(xiàn)動(dòng)畫時(shí)(在drawRect:中實(shí)現(xiàn)起來(lái)真的很麻煩)碴犬。

在這種情況下,我們需要在后臺(tái)渲染所有其他東西梆暮。此外他能在用戶快速滑動(dòng)UITableView時(shí)有效提高FPS服协。

我們Facebook的應(yīng)用。為了檢測(cè)這些啦粹,你可能需要往下滑動(dòng)足夠的高度偿荷,然后點(diǎn)擊狀態(tài)欄窘游。列表會(huì)往上滑動(dòng),因此你可以清楚地看到此時(shí)沒(méi)有渲染cell跳纳,如果想要更精確忍饰,則不能及時(shí)獲得。

這很簡(jiǎn)單寺庄,所以你可以自己試試艾蓝,這時(shí),你需要設(shè)置CALayer的drawsAsynchronously屬性為YES斗塘。

但是我們可以檢查這些行為的必要性赢织,在模擬器上運(yùn)行程序,然后選擇“Debug”菜單中的“Color Offscreen-Rendered”♀擅耍現(xiàn)在所有在后臺(tái)渲染的區(qū)域都被高亮為黃色于置。


如果你為某些層開啟了這一模式,但是它沒(méi)有高亮顯示贞岭,那么它就不夠慢

為了在CALayer層找到瓶頸并進(jìn)一步減少它俱两,你可以使用Instruments里面的Time Profiler。

(關(guān)于Instruments使用工具有Leaks曹步、Allocations宪彩,Time Profiler幫助我們分析代碼的執(zhí)行實(shí)現(xiàn),找出導(dǎo)致程序變慢的原因讲婚,告訴我們‘實(shí)踐都去哪了’)

這里異步優(yōu)化UI的實(shí)現(xiàn)清單:

找到讓你的cell無(wú)法快速返回的瓶頸尿孔。

將操作移到后臺(tái)線程,并在主線程刷新顯示的內(nèi)容筹麸。

最后一招是設(shè)置你的CALayer為異步顯示模式(即使只是簡(jiǎn)單的文本或圖片)這將幫你提高FPS活合。



我嘗試解釋了iOS繪圖系統(tǒng)(沒(méi)有使用OpenGL,因?yàn)樗那闆r更少)的主要思路物赶。

當(dāng)然有些看起來(lái)很模糊白指,但事實(shí)上這只是一些方向,你應(yīng)該朝著這些方向來(lái)檢查你的代碼以找出影響滾動(dòng)性能的所有問(wèn)題酵紫。具體情況具體分期告嘲,但原則是不變的。

獲取完美平滑滾動(dòng)的關(guān)鍵是非常特殊的代碼奖地,他能讓你竭盡iOS的能力讓你的應(yīng)用更加平滑橄唬。完。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末参歹,一起剝皮案震驚了整個(gè)濱河市仰楚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖僧界,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侨嘀,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡捂襟,警方通過(guò)查閱死者的電腦和手機(jī)飒炎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)笆豁,“玉大人郎汪,你說(shuō)我怎么就攤上這事〈秤” “怎么了煞赢?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)哄孤。 經(jīng)常有香客問(wèn)我照筑,道長(zhǎng),這世上最難降的妖魔是什么瘦陈? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任凝危,我火速辦了婚禮,結(jié)果婚禮上晨逝,老公的妹妹穿的比我還像新娘蛾默。我一直安慰自己,他們只是感情好捉貌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布支鸡。 她就那樣靜靜地躺著,像睡著了一般趁窃。 火紅的嫁衣襯著肌膚如雪牧挣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天醒陆,我揣著相機(jī)與錄音瀑构,去河邊找鬼。 笑死刨摩,一個(gè)胖子當(dāng)著我的面吹牛寺晌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播码邻,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼折剃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了像屋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤边篮,失蹤者是張志新(化名)和其女友劉穎己莺,沒(méi)想到半個(gè)月后奏甫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凌受,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年阵子,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胜蛉。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挠进,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出誊册,到底是詐尸還是另有隱情领突,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布案怯,位于F島的核電站君旦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嘲碱。R本人自食惡果不足惜金砍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望麦锯。 院中可真熱鬧恕稠,春花似錦、人聲如沸扶欣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)宵蛀。三九已至昆著,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間术陶,已是汗流浹背凑懂。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梧宫,地道東北人接谨。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像塘匣,于是被迫代替她去往敵國(guó)和親脓豪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容