咱們公司的主App在大約17年5月份先後經歷了一次大版本迭代,迭代以後更換了若干個一級和二級頁面,首頁就在這些個一級頁面以內。 17年大約11月份的時候,咱們的小程序第一個版本正式上線,而後咱們技術的大Leader拿來了小程序給咱們看看,小程序的首頁流暢性確實優於咱們客戶端,因而咱們正式啓動了性能優化。ios
優化的第一步,確定是要明確咱們優化具體的Case,須要達到什麼樣的流暢度?是fps達到60?仍是要內存使用降到一個具體的數字? 討論以後,咱們最終將第一期優化定位爲將首頁的fps優化到60。git
雖說是第一期將目標定位優先優化首頁的流暢度,可是本着有第一次就要有第二次的原則,我仍是將App中的其餘流暢度敏感頁面也Review了一遍代碼,算是給本身留一個優化思考的方向。github
Review代碼發現一些比較顯而易見的問題:算法
PS:由於咱們的IM系統是咱們本身寫的,中間又經歷了公司分家,人員換了好幾茬,因而就致使了在原本架構不合理的基礎上,實現業務和功能的代碼更是屎同樣,因此IM的問題更嚴重。並且咱們已經計劃了對於IM的重構時間表,因此我會在另外一篇Blog裏寫一下個人重構思路。數據庫
一、文本寬高計算、視圖佈局計算 二、文本渲染、圖片解碼、圖形繪製 三、對象建立、對象調整、對象銷燬小程序
一、對象的建立:緩存
對象的建立會分配內存、設置屬性等,會消耗CPU資源。因此儘可能使用輕量對象代替,好比能用CALayer的時候儘可能不用UIView,敏感位置能不用IB儘可能使用純代碼手寫。安全
推遲同一時間建立對象,推薦使用懶加載在須要使用時候建立對象。性能優化
二、對象調整網絡
對 UIView 的這些屬性進行調整時,消耗的資源要遠大於通常的屬性。對此你在應用中,應該儘可能減小沒必要要的屬性修改。
當視圖層次調整時,UIView、CALayer 之間會出現不少方法調用與通知,因此在優化性能時,應該儘可能避免調整視圖層次、添加和移除視圖。
三、對象銷燬
當前類持有大量對象時候,其銷燬時候的資源消耗就很是明顯。建議建立銷燬的異步隊列,將須要銷燬的對象放到隊列中銷燬。
四、佈局計算
佈局計算在UITableView使用中是最多見的消耗資源的地方。建議取到數據以後,異步進行計算佈局並緩存下來,當複用Cell時候直接調用緩存數據。
五、AutoLayout
Autolayout 對於複雜視圖來講經常會產生嚴重的性能問題,AutoLayout相對低效的緣由是隱藏在底層的命名爲」Cassowary「的約束求解系統,隨着視圖數量的增加,Autolayout 帶來的 CPU 消耗會呈指數級上升,當Cell內約束超過25個的時候,會下降滑動的幀率。
具體:pilky.me/36/。
六、文本的計算以及渲染
UI中存在大量的對於文本高度的適配,能夠參考:用 [NSAttributedString boundingRectWithSize:options:context:] 來計算文本寬高,用 -[NSAttributedString drawWithRect:options:context:] 來繪製文本。儘管這兩個方法性能不錯,但仍舊須要放到後臺線程進行以免阻塞主線程。
常見的文本控件 (UILabel、UITextView 等),其排版和繪製都是在主線程進行的,當顯示大量文本時,CPU 的壓力會很是大。解決辦法是利用TextKit或者是CoreText自定義文本控件。詳見:YYText。
七、圖片解碼以及圖像的繪製
當你用 UIImage 或 CGImageSource 的那幾個方法建立圖片時,圖片數據並不會馬上解碼。圖片設置到 UIImageView 或者 CALayer.contents 中去,而且 CALayer 被提交到 GPU 前,CGImage 中的數據纔會獲得解碼。這一步是發生在主線程的,而且不可避免。若是想要繞開這個機制,常見的作法是在後臺線程先把圖片繪製到 CGBitmapContext 中,而後從 Bitmap 直接建立圖片。目前常見的網絡圖片庫都自帶這個功能。
個最多見的地方就是 [UIView drawRect:] 裏面了。因爲 CoreGraphic 方法一般都是線程安全的,因此圖像的繪製能夠很容易的放到後臺線程進行。
八、文件系統的調用
NSFileManager獲取一個目錄獲取文件信息,進行屢次遞歸計算,stat幾乎瞬間完成,NSFileManager耗時較長且消耗CPU。
一、紋理的渲染
當在較短期顯示大量圖片時(好比 TableView 存在很是多的圖片而且快速滑動時),CPU 佔用率很低,GPU 佔用很是高,界面仍然會掉幀。避免這種狀況的方法只能是儘可能減小在短期內大量圖片的顯示,儘量將多張圖片合成爲一張進行顯示。
二、視圖的混合(Blended)
視圖結構過於複雜,混合的過程、會消耗不少 GPU 資源。爲了減輕這種狀況的 GPU 消耗,應用應當儘可能減小視圖數量和層次,並在不透明的視圖裏標明 opaque 屬性以免無用的 Alpha 通道合成。固然,這也能夠用上面的方法,把多個視圖預先渲染爲一張圖片來顯示。
三、圖形的生成
CALayer 的 border、圓角、陰影、遮罩(mask),CASharpLayer 的矢量圖形顯示,一般會觸發離屏渲染(offscreen rendering),而離屏渲染一般發生在 GPU 中。能夠嘗試開啓 CALayer.shouldRasterize 屬性,但這會把本來離屏渲染的操做轉嫁到 CPU 上去。
好的方法是使用圖片遮罩等方法,避免使用圓角和隱形等。詳細:iOS的離屏渲染
一、預先計算UI佈局
獲取數據以後,異步計算Cell高度以及各控件高度和位置,並儲存在CellLayouModel中,當每次Cell須要高度以及內部佈局的時候就能夠直接調用,不須要進行重複計算。
二、使用自動緩存高度
iOS 8以後出現了UITableView經過約束自動計算高度,可是由於iOS對於約束的算法問題,會致使流暢性下降,FDTemplateLayoutCell很好的優化了這個問題。
三、異步繪製
Facebook的開源項目Texture(原AsyncDisplayKit),經過利用ASDisplayNode封裝了CALayer,實現了異步繪製。
第三方微博客戶端墨客的是現實,當滑動時,鬆開手指後,馬上計算出滑動中止時 Cell 的位置,並預先繪製那個位置附近的幾個 Cell,而忽略當前滑動中的 Cell。但也有缺點,快速滑動的時候有可能會出現大量空白。
三、高效圖片加載
四、預加載
列表當中,當滑動到一個能夠設定的位置的時候,提早獲取下載下一頁的數據,並繪製UI。詳見:zhuanlan.zhihu.com/p/23418800。
五、針對Blended Layers以及Misaligned Images
Blended Layers:
Misaligned Images:
現象:
洋紅色:UIView的frame像素不對齊,即不能換算成整數像素值。 黃色:UIImageView的圖片像素大小與其frame.size不對齊,圖片發生了縮放形成。
解決:
下面的狀況或操做會引起離屏渲染:
咱們綜合分析了下現有首頁代碼的代碼結構,發現主要存在問題以下
因而咱們作了如下措施:
性能優化這個東西其實很難造成一個具體的方案,爲何這麼說?由於之因此稱之爲優化,是由於要在原有的代碼基礎上進行優化,原有的代碼又有各式各樣的緣由致使須要依照現有代碼來優化,而很難徹底脫離現有的狀況徹底參照某一種的既定方案進行優化。假如說是徹底參照某一種的方案優化的話,建議仍是將某一個性能敏感的頁面利用Texture進行徹底重寫,這樣才能算是總體化一的利用了某一種方案。