使用layer繪制圓角的時(shí)候回造成界面滑動(dòng)的時(shí)候卡頓志鹃,主要是:
原理
上面拖慢幀率的原因其實(shí)都是Off-Screen Rendering(離屏渲染)的原因泽西。離屏渲染是個(gè)好東西,但是頻繁發(fā)生離屏渲染是非常耗時(shí)的尝苇。
Off-Screen Rendering
離屏渲染,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開辟一個(gè)緩沖區(qū)進(jìn)行渲染操作淳玩。由上面的一個(gè)結(jié)論視圖和圓角的大小對(duì)幀率并沒有什么卵影響非竿,數(shù)量才是傷害的核心輸出啊『熘可以知道離屏渲染耗時(shí)是發(fā)生在離屏這個(gè)動(dòng)作上面,而不是渲染韧骗。為什么離屏這么耗時(shí)零聚?原因主要有創(chuàng)建緩沖區(qū)和上下文切換。創(chuàng)建新的緩沖區(qū)代價(jià)都不算大隶症,付出最大代價(jià)的是上下文切換。
上下文切換
上下文切換淋样,不管是在GPU渲染過程中胁住,還是一直所熟悉的進(jìn)程切換刊咳,上下文切換在哪里都是一個(gè)相當(dāng)耗時(shí)的操作儡司。首先我要保存當(dāng)前屏幕渲染環(huán)境,然后切換到一個(gè)新的繪制環(huán)境枫慷,申請(qǐng)繪制資源浪规,初始化環(huán)境,然后開始一個(gè)繪制誉裆,繪制完畢后銷毀這個(gè)繪制環(huán)境缸濒,如需要切換到On-Screen Rendering或者再開始一個(gè)新的離屏渲染重復(fù)之前的操作。 下圖描述了一次mask的渲染操作庇配。
一次mask發(fā)生了兩次離屏渲染和一次主屏渲染。即使忽略昂貴的上下文切換耀鸦,一次mask需要渲染三次才能在屏幕上顯示啸澡,這已經(jīng)是普通視圖顯示3陪耗時(shí),若再加上下文環(huán)境切換嗅虏,一次mask就是普通渲染的30倍以上耗時(shí)操作。問我這個(gè)30倍以上這個(gè)數(shù)據(jù)怎么的出來(lái)的楞艾?當(dāng)我在cell的UIImageView的實(shí)例增加到150個(gè)冰更,并去掉圓角的時(shí)候,幀數(shù)才跌至28幀每秒蜀细。雖然不是甚準(zhǔn)確,但至少反映mask這個(gè)耗時(shí)是無(wú)mask操作的耗時(shí)的數(shù)十倍的谆刨。
應(yīng)對(duì)
那么如何應(yīng)對(duì)這個(gè)問題呢?不要在滾動(dòng)視圖使用cornerRadius或者mask痊夭。如果你非要作死怎么辦呢?那么這樣也可以拯救你:
self.layer.shouldRasterize=YES;
self.layer.rasterizationScale= [UIScreen mainScreen].scale;
比如我用collectionview做瀑布流虹曙,每一個(gè)item都是一個(gè)圓角照片番舆,即使我們使用了collectionview的重用機(jī)制,也會(huì)出現(xiàn)卡頓現(xiàn)象(雖然在6s上不是很明顯)恨狈,如果重用你都不用的的話,那么真的是沒法用了返奉。但是你如果加上rasterization的話吗氏,就會(huì)好很多的,不信可以試一下弦讽,效果很明顯。
這樣大部分情況下可以馬上挽救你的幀數(shù)在55幀每秒以上十厢。shouldRasterize = YES會(huì)使視圖渲染內(nèi)容被緩存起來(lái)捂齐,下次繪制的時(shí)候可以直接顯示緩存,當(dāng)然要在視圖內(nèi)容不改變的情況下奠宜。
除了上面非要作死的人外,大家還是采取預(yù)先生成圓角圖片娩嚼,并緩存起來(lái)這個(gè)方法才是比較好的手段滴肿。預(yù)處理圓角圖片可以在后臺(tái)處理,處理完畢后緩存起來(lái),再在主線程顯示呵俏,這就避免了不必要的離屏渲染了滔灶。
另外也有在圖片上面覆蓋一個(gè)鏤空?qǐng)A形圖片的方法可以實(shí)現(xiàn)圓形頭像效果,這個(gè)也是極為高效的方法录平。缺點(diǎn)就是對(duì)視圖的背景有要求,單色背景效果就最為理想绪氛。
總結(jié)
實(shí)現(xiàn)圓角cornerRadius要比mask高效很多涝影。
Rasterize在大部分情況下極大減少GPU工作争占。在有空間的情況下,大部分情況下緩存總能幫到你臂痕,不是嗎?
后臺(tái)預(yù)處理圖片也能很簡(jiǎn)單幫上你很大的忙姆怪。