iOS 性能優(yōu)化的探索

起因

我們公司的主App在大約17年5月份前后經(jīng)歷了一次大版本迭代隐解,迭代之后更換了若干個(gè)一級(jí)和二級(jí)頁面退渗,首頁就在這些個(gè)一級(jí)頁面之內(nèi)今艺。
17年大約11月份的時(shí)候挺身,我們的小程序第一個(gè)版本正式上線,然后我們技術(shù)的大Leader拿來了小程序給我們看看消略,小程序的首頁流暢性確實(shí)優(yōu)于我們客戶端堡称,于是我們正式啟動(dòng)了性能優(yōu)化。

明確優(yōu)化的目標(biāo)

優(yōu)化的第一步艺演,肯定是要明確我們優(yōu)化具體的Case却紧,需要達(dá)到什么樣的流暢度婿失?是fps達(dá)到60?還是要內(nèi)存使用降到一個(gè)具體的數(shù)字啄寡?
討論之后豪硅,我們最終將第一期優(yōu)化定位為將首頁的fps優(yōu)化到60。

調(diào)研過程

雖然說是第一期將目標(biāo)定位優(yōu)先優(yōu)化首頁的流暢度挺物,但是本著有第一次就要有第二次的原則懒浮,我還是將App中的其他流暢度敏感頁面也Review了一遍代碼,算是給自己留一個(gè)優(yōu)化思考的方向识藤。

Review代碼發(fā)現(xiàn)一些比較顯而易見的問題:

  • 肆意調(diào)用數(shù)據(jù)庫而沒有用cache
  • 復(fù)雜UI大量使用約束
  • 離屏渲染
  • 像素混合

PS:因?yàn)槲覀兊腎M系統(tǒng)是我們自己寫的砚著,中間又經(jīng)歷了公司分家,人員換了好幾茬痴昧,于是就導(dǎo)致了在本來架構(gòu)不合理的基礎(chǔ)上稽穆,實(shí)現(xiàn)業(yè)務(wù)和功能的代碼更是屎一樣,所以IM的問題更嚴(yán)重赶撰。而且我們已經(jīng)計(jì)劃了對(duì)于IM的重構(gòu)時(shí)間表舌镶,所以我會(huì)在另一篇Blog里寫一下我的重構(gòu)思路。

方案整理

影響流暢度的主要原因:

1豪娜、文本寬高計(jì)算餐胀、視圖布局計(jì)算
2、文本渲染瘤载、圖片解碼否灾、圖形繪制
3、對(duì)象創(chuàng)建鸣奔、對(duì)象調(diào)整墨技、對(duì)象銷毀

CPU資源消耗原因以及解決辦法:

1、對(duì)象的創(chuàng)建:

對(duì)象的創(chuàng)建會(huì)分配內(nèi)存挎狸、設(shè)置屬性等扣汪,會(huì)消耗CPU資源。所以盡量使用輕量對(duì)象代替伟叛,比如能用CALayer的時(shí)候盡量不用UIView私痹,敏感位置能不用IB盡量使用純代碼手寫脐嫂。

推遲同一時(shí)間創(chuàng)建對(duì)象统刮,推薦使用懶加載在需要使用時(shí)候創(chuàng)建對(duì)象。

2账千、對(duì)象調(diào)整

對(duì) UIView 的這些屬性進(jìn)行調(diào)整時(shí)侥蒙,消耗的資源要遠(yuǎn)大于一般的屬性。對(duì)此你在應(yīng)用中匀奏,應(yīng)該盡量減少不必要的屬性修改鞭衩。

當(dāng)視圖層次調(diào)整時(shí),UIView、CALayer 之間會(huì)出現(xiàn)很多方法調(diào)用與通知论衍,所以在優(yōu)化性能時(shí)瑞佩,應(yīng)該盡量避免調(diào)整視圖層次、添加和移除視圖坯台。

3炬丸、對(duì)象銷毀

當(dāng)前類持有大量對(duì)象時(shí)候,其銷毀時(shí)候的資源消耗就非常明顯蜒蕾。建議創(chuàng)建銷毀的異步隊(duì)列稠炬,將需要銷毀的對(duì)象放到隊(duì)列中銷毀。

4咪啡、布局計(jì)算

布局計(jì)算在UITableView使用中是最常見的消耗資源的地方首启。建議取到數(shù)據(jù)之后,異步進(jìn)行計(jì)算布局并緩存下來撤摸,當(dāng)復(fù)用Cell時(shí)候直接調(diào)用緩存數(shù)據(jù)毅桃。

5、AutoLayout

Autolayout 對(duì)于復(fù)雜視圖來說常常會(huì)產(chǎn)生嚴(yán)重的性能問題准夷,AutoLayout相對(duì)低效的原因是隱藏在底層的命名為”Cassowary“的約束求解系統(tǒng)疾嗅,隨著視圖數(shù)量的增長,Autolayout 帶來的 CPU 消耗會(huì)呈指數(shù)級(jí)上升冕象,當(dāng)Cell內(nèi)約束超過25個(gè)的時(shí)候代承,會(huì)降低滑動(dòng)的幀率。

具體:http://pilky.me/36/渐扮。

6论悴、文本的計(jì)算以及渲染

UI中存在大量的對(duì)于文本高度的適配,可以參考:用 [NSAttributedString boundingRectWithSize:options:context:] 來計(jì)算文本寬高墓律,用 -[NSAttributedString drawWithRect:options:context:] 來繪制文本膀估。盡管這兩個(gè)方法性能不錯(cuò),但仍舊需要放到后臺(tái)線程進(jìn)行以避免阻塞主線程耻讽。

常見的文本控件 (UILabel察纯、UITextView 等),其排版和繪制都是在主線程進(jìn)行的针肥,當(dāng)顯示大量文本時(shí)饼记,CPU 的壓力會(huì)非常大。解決辦法是利用TextKit或者是CoreText自定義文本控件慰枕。詳見:YYText具则。

7、圖片解碼以及圖像的繪制

當(dāng)你用 UIImage 或 CGImageSource 的那幾個(gè)方法創(chuàng)建圖片時(shí)具帮,圖片數(shù)據(jù)并不會(huì)立刻解碼博肋。圖片設(shè)置到 UIImageView 或者 CALayer.contents 中去低斋,并且 CALayer 被提交到 GPU 前,CGImage 中的數(shù)據(jù)才會(huì)得到解碼匪凡。這一步是發(fā)生在主線程的膊畴,并且不可避免。如果想要繞開這個(gè)機(jī)制病游,常見的做法是在后臺(tái)線程先把圖片繪制到 CGBitmapContext 中巴比,然后從 Bitmap 直接創(chuàng)建圖片。目前常見的網(wǎng)絡(luò)圖片庫都自帶這個(gè)功能礁遵。

個(gè)最常見的地方就是 [UIView drawRect:] 里面了轻绞。由于 CoreGraphic 方法通常都是線程安全的,所以圖像的繪制可以很容易的放到后臺(tái)線程進(jìn)行佣耐。

8政勃、文件系統(tǒng)的調(diào)用

NSFileManager獲取一個(gè)目錄獲取文件信息,進(jìn)行多次遞歸計(jì)算兼砖,stat幾乎瞬間完成奸远,NSFileManager耗時(shí)較長且消耗CPU。

GPU資源消耗原因以及解決辦法:

1讽挟、紋理的渲染

當(dāng)在較短時(shí)間顯示大量圖片時(shí)(比如 TableView 存在非常多的圖片并且快速滑動(dòng)時(shí))懒叛,CPU 占用率很低,GPU 占用非常高耽梅,界面仍然會(huì)掉幀薛窥。避免這種情況的方法只能是盡量減少在短時(shí)間內(nèi)大量圖片的顯示,盡可能將多張圖片合成為一張進(jìn)行顯示眼姐。

2诅迷、視圖的混合(Blended)

視圖結(jié)構(gòu)過于復(fù)雜,混合的過程众旗、會(huì)消耗很多 GPU 資源罢杉。為了減輕這種情況的 GPU 消耗,應(yīng)用應(yīng)當(dāng)盡量減少視圖數(shù)量和層次贡歧,并在不透明的視圖里標(biāo)明 opaque 屬性以避免無用的 Alpha 通道合成滩租。當(dāng)然,這也可以用上面的方法利朵,把多個(gè)視圖預(yù)先渲染為一張圖片來顯示律想。

  1. Blended Layers(視圖混合):在同一個(gè)區(qū)域內(nèi),存在著多個(gè)有透明度的圖層哗咆,那么GPU需要更多的計(jì)算蜘欲,混合上下多個(gè)圖層才能得出最終像素的RGB值益眉。
  2. Misaligned Images(像素對(duì)齊):邏輯像素(point)和 物理像素(pixel)無法相匹配晌柬;圖片的size和顯示圖片的imageView的size(邏輯像素(point))不相等姥份。

3、圖形的生成

CALayer 的 border年碘、圓角澈歉、陰影、遮罩(mask)屿衅,CASharpLayer 的矢量圖形顯示埃难,通常會(huì)觸發(fā)離屏渲染(offscreen rendering),而離屏渲染通常發(fā)生在 GPU 中涤久∥谐荆可以嘗試開啟 CALayer.shouldRasterize 屬性,但這會(huì)把原本離屏渲染的操作轉(zhuǎn)嫁到 CPU 上去响迂。

好的方法是使用圖片遮罩等方法考抄,避免使用圓角和隱形等。詳細(xì):iOS的離屏渲染

解決方案:

1蔗彤、預(yù)先計(jì)算UI布局

獲取數(shù)據(jù)之后川梅,異步計(jì)算Cell高度以及各控件高度和位置,并儲(chǔ)存在CellLayouModel中然遏,當(dāng)每次Cell需要高度以及內(nèi)部布局的時(shí)候就可以直接調(diào)用贫途,不需要進(jìn)行重復(fù)計(jì)算。

2待侵、使用自動(dòng)緩存高度

iOS 8之后出現(xiàn)了UITableView通過約束自動(dòng)計(jì)算高度丢早,但是因?yàn)閕OS對(duì)于約束的算法問題,會(huì)導(dǎo)致流暢性降低秧倾,FDTemplateLayoutCell很好的優(yōu)化了這個(gè)問題香拉。

3、異步繪制

Facebook的開源項(xiàng)目Texture(原AsyncDisplayKit)中狂,通過利用ASDisplayNode封裝了CALayer凫碌,實(shí)現(xiàn)了異步繪制。

第三方微博客戶端墨客的是現(xiàn)實(shí)胃榕,當(dāng)滑動(dòng)時(shí)盛险,松開手指后,立刻計(jì)算出滑動(dòng)停止時(shí) Cell 的位置勋又,并預(yù)先繪制那個(gè)位置附近的幾個(gè) Cell苦掘,而忽略當(dāng)前滑動(dòng)中的 Cell。但也有缺點(diǎn)楔壤,快速滑動(dòng)的時(shí)候有可能會(huì)出現(xiàn)大量空白鹤啡。

3、高效圖片加載

4蹲嚣、預(yù)加載

列表當(dāng)中递瑰,當(dāng)滑動(dòng)到一個(gè)可以設(shè)定的位置的時(shí)候祟牲,提前獲取下載下一頁的數(shù)據(jù),并繪制UI抖部。詳見:https://zhuanlan.zhihu.com/p/23418800说贝。

5、針對(duì)Blended Layers以及Misaligned Images

Blended Layers:

  1. 盡量少的使用具有透明色的圖片
  2. 盡量多的將UI部件增加背景色
  3. 減少同一像素點(diǎn)進(jìn)行過多的顏色計(jì)算

Misaligned Images:

現(xiàn)象:

洋紅色:UIView的frame像素不對(duì)齊慎颗,即不能換算成整數(shù)像素值乡恕。
黃色:UIImageView的圖片像素大小與其frame.size不對(duì)齊,圖片發(fā)生了縮放造成俯萎。

解決:

  1. 盡量使用ceil()傲宜,保證沒有小數(shù)的UI繪制
  2. 盡量不實(shí)用0.01f標(biāo)記UITableView或者UICollectionView的header以及footer
  3. 網(wǎng)絡(luò)上獲取的圖片沒有@2x和@3x的區(qū)別,需要我們縮放圖片到與UIImageView對(duì)應(yīng)的尺寸夫啊,且縮放后的圖片的scale和[UIScreen mainScreen].scale相等蛋哭,再顯示出來。

其他:

下面的情況或操作會(huì)引發(fā)離屏渲染:

  • 為圖層設(shè)置遮罩(layer.mask)
  • 將圖層的layer.masksToBounds / view.clipsToBounds屬性設(shè)置為true
  • 將圖層layer.allowsGroupOpacity屬性設(shè)置為YES和layer.opacity小于1.0
  • 為圖層設(shè)置陰影(layer.shadow *)涮母。
  • 為圖層設(shè)置layer.shouldRasterize=true
  • 具有l(wèi)ayer.cornerRadius谆趾,layer.edgeAntialiasingMask,layer.allowsEdgeAntialiasing的圖層
  • 文本(任何種類叛本,包括UILabel沪蓬,CATextLayer,Core Text等)来候。
  • 使用CGContext在drawRect :方法中繪制大部分情況下會(huì)導(dǎo)致離屏渲染跷叉,甚至僅僅是一個(gè)空的實(shí)現(xiàn)。

開工

我們綜合分析了下現(xiàn)有首頁代碼的代碼結(jié)構(gòu)营搅,發(fā)現(xiàn)主要存在問題如下

  • 較多的使用約束進(jìn)行布局
  • 每次Cell滑動(dòng)進(jìn)入屏幕都需要根據(jù)DataSource計(jì)算一次Cell高度云挟,浪費(fèi)時(shí)間。
  • 像素混合等問題嚴(yán)重转质。
  • 存在離屏渲染园欣。
  • 無預(yù)加載邏輯,導(dǎo)致滑動(dòng)流暢性降低休蟹。

于是我們做了以下措施:

  • 使用frame布局來替換約束布局沸枯。
  • 每次拉取到數(shù)據(jù)之后,異步計(jì)算Cell高度并做為單獨(dú)字段緩存在DataSource中赂弓。
  • 按照代碼規(guī)范绑榴,規(guī)避像素混合問題和離屏渲染。
  • 在相應(yīng)位置增加了預(yù)先拉取數(shù)據(jù)的邏輯盈魁,實(shí)現(xiàn)效果是用戶沒有將UI滑動(dòng)到最后一條的時(shí)候翔怎,數(shù)據(jù)以及完成了拉取并刷新UI的邏輯。

總結(jié)

性能優(yōu)化這個(gè)東西其實(shí)很難形成一個(gè)具體的方案,為什么這么說赤套?因?yàn)橹苑Q之為優(yōu)化飘痛,是因?yàn)橐谠械拇a基礎(chǔ)上進(jìn)行優(yōu)化,原有的代碼又有各式各樣的原因?qū)е滦枰勒宅F(xiàn)有代碼來優(yōu)化于毙,而很難完全脫離現(xiàn)有的情況完全參照某一種的既定方案進(jìn)行優(yōu)化敦冬。假如說是完全參照某一種的方案優(yōu)化的話辅搬,建議還是將某一個(gè)性能敏感的頁面利用Texture進(jìn)行完全重寫唯沮,這樣才能算是整體化一的利用了某一種方案。


Refrence


另外

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末介蛉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子溶褪,更是在濱河造成了極大的恐慌币旧,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件猿妈,死亡現(xiàn)場離奇詭異吹菱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)彭则,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門鳍刷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人俯抖,你說我怎么就攤上這事输瓜。” “怎么了芬萍?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵尤揣,是天一觀的道長。 經(jīng)常有香客問我柬祠,道長北戏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任漫蛔,我火速辦了婚禮最欠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惩猫。我一直安慰自己芝硬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布轧房。 她就那樣靜靜地躺著拌阴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪奶镶。 梳的紋絲不亂的頭發(fā)上迟赃,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天陪拘,我揣著相機(jī)與錄音,去河邊找鬼纤壁。 笑死左刽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酌媒。 我是一名探鬼主播欠痴,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼秒咨!你這毒婦竟也來了喇辽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤雨席,失蹤者是張志新(化名)和其女友劉穎菩咨,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體陡厘,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抽米,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了糙置。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片云茸。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖罢低,靈堂內(nèi)的尸體忽然破棺而出查辩,到底是詐尸還是另有隱情,我是刑警寧澤网持,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布宜岛,位于F島的核電站,受9級(jí)特大地震影響功舀,放射性物質(zhì)發(fā)生泄漏萍倡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一辟汰、第九天 我趴在偏房一處隱蔽的房頂上張望列敲。 院中可真熱鬧,春花似錦帖汞、人聲如沸戴而。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽所意。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扶踊,已是汗流浹背泄鹏。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留秧耗,地道東北人备籽。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像分井,于是被迫代替她去往敵國和親车猬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345