[轉(zhuǎn)自] http://supermao.cn/xiao-xin-bie-rang-yuan-jiao-cheng-liao-ni-lie-biao-de-zheng-shu-sha-shou/#rd?sukey=3fed89b912c7bbb2afd9c1d1fe4dda38796f471b2f6205a4af2fccd04b26a7bdf186785bc36fa2ad554bd7b458a4de69
前言
在iOS的世界,圓角無處不在间影,而且必須存在凯正。因為圓角是符合人類視覺安全體驗的柱衔,圓角讓人覺得舒適姑隅,而方角在潛意識層次是具有傷害體驗的,因為尖尖的東西總是有可能對人造成傷害的南片,所以我們更喜歡圓角眼滤。在我之前的文章中講過,在iOS的中設(shè)置圓角是非常容易的一件事情大猛,這也體現(xiàn)出蘋果也是非常重視圓角這件事情的扭倾。
圓角雖好,但如果使用不當(dāng)挽绩,它就是你的幀數(shù)殺手膛壹,特別當(dāng)它出現(xiàn)在滾動列表的時候。下面來看圓角如何毀掉你的流暢度的。
實測
layer.cornerRadius
我創(chuàng)建了一個簡單地UITableView視圖模聋,為每個cell添加了2個UIImageView實例肩民,且為UIImageView實例進行如下設(shè)置
aImageView.layer.cornerRadius = aImageView.frame.size.width/2.0; aImageView.layer.masksToBounds = YES;
運行截圖如下:
你們猜,現(xiàn)在滾動的幀率是多少链方。
已經(jīng)跌至45幀每秒持痰,這個幀率已經(jīng)讓人感覺到不那么順滑了,如果低于40幀每秒祟蚀,普通用戶就會察覺明顯的不流暢了工窍。當(dāng)我把cell的UIImageView實例增加至四個
現(xiàn)在幀率已經(jīng)低于30幀每秒了
這個幀率如果出現(xiàn)在首屏,足以引領(lǐng)你的app進入垃圾級別的體驗了≡萏猓現(xiàn)在我把UIImageView實例的size調(diào)的小一些移剪。
平均幀率提高了大概3幀每秒。
在這里視圖和圓角的大小對幀率并沒有什么卵影響薪者,數(shù)量才是傷害的核心輸出啊纵苛。
layer.mask
之前有的文章說通過layer.cornerRadius和layer.mask設(shè)置圓角并沒有什么差異言津,事實真的是這樣的嗎攻人?我如下設(shè)置了圓角:
CAShapeLayer *layer = [CAShapeLayer layer]; UIBezierPath *aPath = [UIBezierPath bezierPathWithOvalInRect:aImageView.bounds]; layer.path = aPath.CGPath; aImageView.layer.mask = layer;
得到的幀率如下:
竟然只有20幀每秒了,比layer.cornerRadius還少了8幀;澄恰!初婆!所以layer.cornerRadius實現(xiàn)圓角的性能是要比layer.mask要高很多蓬坡。
maskView
iOS的UIView多了一個maskView方法屑咳,不過這個東西和layer.mask是一個卵樣的敲董。
原理
上面拖慢幀率的原因其實都是Off-Screen Rendering
(離屏渲染)的原因聪铺。離屏渲染是個好東西计寇,但是頻繁發(fā)生離屏渲染是非常耗時的赖阻。
Off-Screen Rendering
離屏渲染,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開辟一個緩沖區(qū)進行渲染操作。由上面的一個結(jié)論視圖和圓角的大小對幀率并沒有什么卵影響于未,數(shù)量才是傷害的核心輸出啊烘浦。
可以知道離屏渲染耗時是發(fā)生在離屏這個動作上面握侧,而不是渲染品擎。為什么離屏
這么耗時坦喘?原因主要有創(chuàng)建緩沖區(qū)
和上下文切換
。創(chuàng)建新的緩沖區(qū)代價都不算大梦碗,付出最大代價的是上下文切換。
上下文切換
上下文切換斩例,不管是在GPU渲染過程中础钠,還是一直所熟悉的進程切換,上下文切換在哪里都是一個相當(dāng)耗時的操作翻具。首先我要保存當(dāng)前屏幕渲染環(huán)境叹洲,然后切換到一個新的繪制環(huán)境民泵,申請繪制資源,初始化環(huán)境,然后開始一個繪制霞扬,繪制完畢后銷毀這個繪制環(huán)境萤彩,如需要切換到On-Screen Rendering或者再開始一個新的離屏渲染重復(fù)之前的操作。下圖描述了一次mask的渲染操作。
一次mask發(fā)生了兩次離屏渲染和一次主屏渲染硼啤。即使忽略昂貴的上下文切換籍救,一次mask需要渲染三次才能在屏幕上顯示梧却,這已經(jīng)是普通視圖顯示3陪耗時,若再加上下文環(huán)境切換伴郁,一次mask就是普通渲染的30倍以上
耗時操作晕鹊。問我這個30倍以上
這個數(shù)據(jù)怎么的出來的?當(dāng)我在cell的UIImageView的實例增加到150個卵史,并去掉圓角的時候寸潦,幀數(shù)才跌至28幀每秒易核。雖然不是甚準(zhǔn)確浪默,但至少反映mask這個耗時是無mask操作的耗時的數(shù)十倍的牡直。
應(yīng)對
那么如何應(yīng)對這個問題呢缀匕?不要在滾動視圖使用cornerRadius或者mask。如果你非要作死怎么辦呢碰逸?那么這樣也可以拯救你:
self.layer.shouldRasterize = YES; self.layer.rasterizationScale = [UIScreen mainScreen].scale;
這樣大部分情況下可以馬上挽救你的幀數(shù)在55幀每秒以上乡小。shouldRasterize = YES
會使視圖渲染內(nèi)容被緩存起來,下次繪制的時候可以直接顯示緩存饵史,當(dāng)然要在視圖內(nèi)容不改變的情況下满钟。
除了上面非要作死的人外,大家還是采取預(yù)先生成圓角圖片胳喷,并緩存起來這個方法才是比較好的手段湃番。預(yù)處理圓角圖片可以在后臺處理,處理完畢后緩存起來厌蔽,再在主線程顯示牵辣,這就避免了不必要的離屏渲染了。
另外也有在圖片上面覆蓋一個鏤空圓形圖片的方法可以實現(xiàn)圓形頭像效果奴饮,這個也是極為高效的方法。缺點就是對視圖的背景有要求择浊,單色背景效果就最為理想戴卜。
總結(jié)
實現(xiàn)圓角cornerRadius要比mask高效很多。
Rasterize在大部分情況下極大減少GPU工作琢岩。在有空間的情況下投剥,大部分情況下緩存總能幫到你,不是嗎担孔?
后臺預(yù)處理圖片也能很簡單幫上你很大的忙江锨。
(以上測試數(shù)據(jù)來自于iPhone5。)