轉載:當心別讓圓角成了你列表的幀數殺手

前言
在iOS的世界,圓角無處不在,並且必須存在。由於圓角是符合人類視覺安全體驗的,圓角讓人以爲溫馨,而方角在潛意識層次是具備傷害體驗的,由於尖尖的東西老是有可能對人形成傷害的,因此咱們更喜歡圓角。在我以前的文章中講過,在iOS的中設置圓角是很是容易的一件事情,這也體現出蘋果也是很是重視圓角這件事情的。緩存

圓角雖好,但若是使用不當,它就是你的幀數殺手,特別當它出如今滾動列表的時候。下面來看圓角如何毀掉你的流暢度的。安全

實測
layer.cornerRadius
我建立了一個簡單地UITableView視圖,爲每一個cell添加了2個UIImageView實例,且爲UIImageView實例進行以下設置app

aImageView.layer.cornerRadius = aImageView.frame.size.width/2.0;
aImageView.layer.masksToBounds = YES;性能

大家猜,如今滾動的幀率是多少。測試

已經跌至45幀每秒,這個幀率已經讓人感受到不那麼順滑了,若是低於40幀每秒,普通用戶就會察覺明顯的不流暢了。當我把cell的UIImageView實例增長至四個線程

如今幀率已經低於30幀每秒了進程

這個幀率若是出如今首屏,足以引領你的app進入垃圾級別的體驗了。 如今我把UIImageView實例的size調的小一些。圖片

平均幀率提升了大概3幀每秒。資源

在這裏視圖和圓角的大小對幀率並無什麼卵影響,數量纔是傷害的核心輸出啊。get

layer.mask
以前有的文章說經過layer.cornerRadius和layer.mask設置圓角並無什麼差別,事實真的是這樣的嗎?我以下設置了圓角:

CAShapeLayer *layer = [CAShapeLayer layer];
UIBezierPath *aPath = [UIBezierPath bezierPathWithOvalInRect:aImageView.bounds];
layer.path = aPath.CGPath;
aImageView.layer.mask = layer;
獲得的幀率以下:

居然只有20幀每秒了,比layer.cornerRadius還少了8幀!!!因此layer.cornerRadius實現圓角的性能是要比layer.mask要高不少。

maskView
iOS的UIView多了一個maskView方法,不過這個東西和layer.mask是一個卵樣的。

原理
上面拖慢幀率的緣由其實都是Off-Screen Rendering(離屏渲染)的緣由。離屏渲染是個好東西,可是頻繁發生離屏渲染是很是耗時的。

Off-Screen Rendering
離屏渲染,指的是GPU在當前屏幕緩衝區之外新開闢一個緩衝區進行渲染操做。由上面的一個結論視圖和圓角的大小對幀率並無什麼卵影響,數量纔是傷害的核心輸出啊。能夠知道離屏渲染耗時是發生在離屏這個動做上面,而不是渲染。爲何離屏這麼耗時?緣由主要有建立緩衝區和上下文切換。建立新的緩衝區代價都不算大,付出最大代價的是上下文切換。

上下文切換
上下文切換,不論是在GPU渲染過程當中,仍是一直所熟悉的進程切換,上下文切換在哪裏都是一個至關耗時的操做。首先我要保存當前屏幕渲染環境,而後切換到一個新的繪製環境,申請繪製資源,初始化環境,而後開始一個繪製,繪製完畢後銷燬這個繪製環境,如須要切換到On-Screen Rendering或者再開始一個新的離屏渲染重複以前的操做。 下圖描述了一次mask的渲染操做。

一次mask發生了兩次離屏渲染和一次主屏渲染。即便忽略昂貴的上下文切換,一次mask須要渲染三次才能在屏幕上顯示,這已是普通視圖顯示3陪耗時,若再加上下文環境切換,一次mask就是普通渲染的30倍以上耗時操做。問我這個30倍以上這個數據怎麼的出來的?當我在cell的UIImageView的實例增長到150個,並去掉圓角的時候,幀數才跌至28幀每秒。雖然不是甚準確,但至少反映mask這個耗時是無mask操做的耗時的數十倍的。

應對
那麼如何應對這個問題呢?不要在滾動視圖使用cornerRadius或者mask。若是你非要做死怎麼辦呢?那麼這樣也能夠拯救你:

self.layer.shouldRasterize = YES;
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
這樣大部分狀況下能夠立刻挽救你的幀數在55幀每秒以上。shouldRasterize = YES會使視圖渲染內容被緩存起來,下次繪製的時候能夠直接顯示緩存,固然要在視圖內容不改變的狀況下。

除了上面非要做死的人外,你們仍是採起預先生成圓角圖片,並緩存起來這個方法纔是比較好的手段。預處理圓角圖片能夠在後臺處理,處理完畢後緩存起來,再在主線程顯示,這就避免了沒必要要的離屏渲染了。

另外也有在圖片上面覆蓋一個鏤空圓形圖片的方法能夠實現圓形頭像效果,這個也是極爲高效的方法。缺點就是對視圖的背景有要求,單色背景效果就最爲理想。

總結
實現圓角cornerRadius要比mask高效不少。
Rasterize在大部分狀況下極大減小GPU工做。在有空間的狀況下,大部分狀況下緩存總能幫到你,不是嗎?
後臺預處理圖片也能很簡單幫上你很大的忙。
(以上測試數據來自於iPhone5。)


原文連接:當心別讓圓角成了你列表的幀數殺手

相關文章
相關標籤/搜索