首先我們需要明確一下刷幀率的概念,玩游戲的人對FPS這個詞一般不會陌生,刷幀率代表設備在1s內刷新顯示圖像的幀數,iphone推薦的刷幀率是60,即16.7ms內刷新一幀。
這里借用一下ibireme大神的插圖來介紹一下刷新一幀圖像系統都做了哪些事情 
首先CPU會計算顯示內容,包括布局的計算、圖片解碼、文本的預先繪制(繪制成GPU渲染需要的紋理資源),隨后由GPU進行變換、合成等一些光影的計算,最后將渲染結果提交到renderBuffer,等待接收到上一幀圖像顯示完畢后發來的垂直同步信號后進行顯示。如果接收到垂直同步信號后CPU或者GPU沒有完成計算和內容的提交,那這一幀就會丟失,等待下一次機會再顯示,映射到屏幕上,便會出現卡頓現象。
耗費CPU及GPU資源的具體操作可以查看大神的原文,(他是YYKit的作者,也是一個動漫愛好者,博客中的其他文章也相當不錯,對iOS中一些技術的理解很有深度。)文章中還介紹了Facebook出品的優化界面流暢度的框架AsyncDisplayKit,YYKit中關于優化部分的靈感亦是來自于ASDK,
實際操作時可以使用YYKit針對正常的文字圖片等視圖展示進行優化,除此之外,還有一些其他不合理的操作需要避免。
一塊覆蓋多層layer的區域當上層圖層透明度不為1時,在顯示時則綜合多個圖層的顏色混合進行顯示,使用instrument調試時可以發現,這種Color Blended Layers是需要消耗一定GPU資源的,所以盡量將控件設置成不透明的。(這里說的既包含控件的背景色,也包含控件上顯示的圖片)

上圖是正常的渲染通道,通過OpenGL將需要展示的buffer從CPU內存復制到GPU,GPU經過渲染計算后將渲染結果放到renderBuffer中。再看一下離屏渲染

在前兩個渲染通道中,GPU分別得到了渲染結果,但是并沒有直接放入RenderBuffer中,而是等到第三個通道開始渲染時,將兩者組合,放入renderBuffer中,這種臨時性的保存無疑會占用更多的GPU資源。可能會觸發離屏渲染的操作包括:
重寫drawRect方法使用View的layer.masksToBounds、layer.shadow屬性時高斯模糊效果開啟光柵化layer.shouldRasterize = true
所謂光柵化,是將一個layer預先渲染成bitmap位圖,然后加入緩存中,對于比較消耗資源的靜態內容進行緩存,可以得到小幅性能提升。這個預先緩存的有效期只有100ms,如果在100ms內沒有被使用則會被清除。除了會觸發離屏渲染,在實際使用時經常會出現未命中緩存的現象,且預先緩存有可能會消耗更多的時間,所以要慎用。
最徹底的避免辦法,就是把需要顯示的圖形在后臺線程繪制為圖片,避免使用圓角、陰影、遮罩等屬性。
cell中美工出的圖盡可能使用恰當的大小、格式,對于圖片的縮放顯示和不同格式間的圖片編解碼(iOS中切圖一般用png格式)會耗費一些額外的性能,而且格式轉換還可能會產生一些莫名其妙的問題(比如帶有帶有透明像素的圖像從png轉到jpg時透明像素會變成白色像素)。
當大量需要圓角和陰影效果時不能直接設置 layer.cornerRadius 和 layer.masksToBounds 屬性,同樣不可以直接重寫drawRect方法繪制一個圓角layer然后使用maskLayer屬性來裁剪控件,這和直接使用maskToBounds一樣都會觸發離屏渲染。 最簡單的方法是讓設計師直接給一個圓角的圖片背景,也可以開啟一個位圖上下文動態的繪制一個圓角矩形然后導出UIImage設置為視圖背景。
為UIImageView設置圓角時,則可以直接將需要顯示的圖片繪制成一個圓角圖片然后再設置到ImageView中顯示。但是使用以上方法時記得設置控件的背景色為透明。
使用xcode內置的Instrument可以很方便的進行程序的性能測試,包括內存泄露、處理器使用情況等。測試界面刷幀性能時我們使用Core Animation。

上圖中左邊的frames per second是每秒鐘顯示圖像幀數,即刷幀率,右下角勾選相應的選項可以將發生相應事件的部分在手機屏幕上以不同的顏色標注出來,比如我們勾選了Color Blend layer,手機屏幕上會將發生混合圖層的區域以紅色標記出來。下面兩張圖片可以看出,我拉出手機下方的半透明菜單欄后,處于半透明遮罩下的整個屏幕都發生了顏色混合。(黃色代表發生離屏渲染,圖中的黃色區域是中文的原因,Stackoverflow上有人給出的解釋是,如果文字是中文,label會有一個子layer,導致圖層混合)


由此,我們可以定位一些影響性能的操作從而逐一解決。
新聞熱點
疑難解答