app 性能优化的那些事(二)
?
來源:樹下的老男孩?
鏈接:http://www.jianshu.com/p/2a01e5e2141f
?
這次我們來說說iOS app中滑動的那些事。iOS為了提高滑動的流暢感,特意在滑動的時候將runloop模式切換到UITrackingRunLoopMode,在這個過程中專心做跟滑動相關的工作,這也就是在滑動過程中為什么nstimer無法工作的原因,因為兩個沒在同一mode下面。但我們可能經(jīng)常會遇到滑動不怎么流暢的情況,比如在項目中碰到在滑動tableview的時候不怎么順暢,感覺有點不爽,即便是在測試中表現(xiàn)最好的5s(touch之類的感受更直觀)。
?
tableview 滑動不流暢
那碰到這種情況該怎么處理,分析圖像動畫性能主要用的是Core Animation這個組件,先簡單介紹一下里面一些經(jīng)常用到的選項:
?
-
Color Blended layers
標示混合的圖層會為紅色,不透明的圖層為綠色,通常我們希望綠色的區(qū)域越多越好。
-
Color Hits Green and Misses Red
假如我們設置viewlayer的shouldRasterize為YES,那些成功被緩存的layer會標注為綠色,反之為紅色,下面會有詳細介紹。
-
Color copied images
標示那些被Core Animation拷貝的圖片。這主要是因為該圖片的色彩格式不能被GPU直接處理,需要在CPU這邊做轉換,假如在主線層做這個操作對性能會有一定的影響。
-
Color misaligned images
被縮放的圖片會被標記為黃色,像素不對齊則會標注為紫色。
-
Color offscreen-rendered yellow
標示哪些layer需要做離屏渲染(offscreen-render)。
?
?
簡單介紹完Core Animation的一些東西之后我們回過頭來看看哪些問題會影響到圖形的性能,下面這張圖摘自WWDC2014(Advanced Graphics and Animations for iOS Apps,這上面的一些分享非常有技術性)
?
performance investigation mindset.png
當你碰到性能問題的時候,你可以思考一下:
?
是否受到CPU或者GPU的限制?
是否有不必要的CPU渲染?
是否有太多的離屏渲染操作?
是否有太多的圖層混合操作?
是否有奇怪的圖片格式或者尺寸?
是否涉及到昂貴的view或者效果?
view的層次結構是否合理?
?
那么哪些是你最該開始考慮的方向呢?通常發(fā)生圖形性能問題的時候,比如列表滑動不順暢、動畫卡頓等,大部分都是由于Offscreen Rendering(離屏渲染)或者blending導致的,因為這在動畫的每一幀都會涉及到。
?
offscreen-render
?
什么是offscreen-render?offscreen-render涉及的內(nèi)容比較多,有offscreen-render那就有onscreen render,onscreen render指的是GPU在當前用于顯示的屏幕緩沖區(qū)進行渲染,相反offscreen-render就是不在當前的屏幕緩存區(qū),而在另外的緩沖區(qū)進行渲染,offscreen-render有兩種形式:
?
CPU的offscreen-render
?
使用CPU來完成渲染操縱,通常在你使用:
?
-
drawRect (如果沒有自定義繪制的任務就不要在子類中寫一個空的drawRect方法,因為只要實現(xiàn)了該方法,就會為視圖分配一個寄宿圖,這個寄宿圖的像素尺寸等于視圖大小乘以 contentsScale的值,造成資源浪費)
-
使用Core Graphics
上面的兩種情況使用的就是CPU離屏渲染,首先分配一塊內(nèi)存,然后進行渲染操作生成一份bitmap位圖,整個渲染過程會在你的應用中同步的進行,接著再將位圖打包發(fā)送到iOS里一個單獨的進程–render server,理想情況下,render server將內(nèi)容交給GPU直接顯示到屏幕上。
?
GPU的offscreen-render
?
使用GPU在當前屏幕緩沖區(qū)以外開辟一個新的緩沖區(qū)進行繪制,通常發(fā)生的情況有:
?
-
設置cornerRadius, masks, shadows,edge antialiasing等
-
設置layer.shouldRasterize = YES
?
渲染流程
offscreen-render對性能到底有什么影響?通常大家說的離屏渲染指的是GPU這塊(當然CPU這塊也會有影響,也需要消耗一定的資源),比如修改了layer的陰影或者圓角,GPU需要做額外的渲染操作。通常GPU在做渲染的時候是很快的,但是涉及到offscreen-render的時候情況就可能有些不同,因為需要額外開辟一個新的緩沖區(qū)進行渲染,然后繪制到當前屏幕的過程需要做onscreen跟offscreen上下文之間的切換,這個過程的消耗會比較昂貴,涉及到OpenGL的pipeline跟barrier,而且offscreen-render在每一幀都會涉及到,因此處理不當肯定會對性能產(chǎn)生一定的影響,所以可以的話盡量減少offscreen-render的圖層,查看哪些圖層需要離屏渲染可以用Instruments的Core Animation工具進行檢測,Color Offscreen-Rendered Yellow選項會將對應的圖層標記為黃色。
?
Blending
?
假如最上層的view是不透明的,那直接使用這個view的對應顏色之就可以,但如果view是透明的,在計算像素的顏色值時就需要計算它下面圖層,透明的視圖越多,計算量就越大,因此也會對圖形的性能產(chǎn)生一定的影響,所以可以的話也盡量減少透明圖層的數(shù)目。
?
Demo
?
下面給出一個簡單demo(https://github.com/FreeMind-LJ/OptimiseDemo)的優(yōu)化過程,這個demo里面涉及到的問題是在實際項目中所碰到的,也就是最上面那張圖里列表滑動不流暢情況—由陰影以及圓角導致的offscreen-render。
整個頁面就是一個簡單的tableview,其中頭像為圓角,一個label有陰影效果,滑動的時候在iPod上幀率只有可憐的28FPS。
?
Color Offscreen-Rendered Yellow
28FPS.png
其中黃色的區(qū)域就是離屏渲染的地方,也就是含有圓角跟陰影的layer。
?
shadowPath
?
設置label的陰影效果可以通過:
?
cell.sign.layer.shadowOffset = CGSizeMake(0, 2);
????cell.sign.layer.shadowOpacity = 0.5;
????cell.sign.layer.shadowColor = [UIColor blackColor].CGColor;
?
但是你可以發(fā)現(xiàn)這會導致離屏渲染,一個簡單的不需要離屏渲染的方法就是制定陰影的路徑,也就是設置layer的shadowPath屬性,通過instruments發(fā)現(xiàn)陰影的地方?jīng)]有黃色了,幀率也提高到了40FPS:
?
cell.sign.layer.shadowPath = [UIBezierPath??bezierPathWithRect:cell.sign.bounds].CGPath;
?
設置shadowPath消除離屏渲染.png
?
rasterize
?
對于圓角這種類似導致的性能問題,最簡單的就是在列表中不要使用圓角,假如要使用圓角的話,一種最快提升性能的方式就是設置layer的shouldRasterize為YES:
?
cell.layer.shouldRasterize = YES;
????cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
?
雖然被Rasterize的圖層也會引起離屏渲染,如下圖所示,整個cell都被標示為黃色:
?
shouldRasterize.png
layer設置shouldRasterize=YES之后,會把被光柵化的圖層保存成位圖并緩存起來,其中圓角或者陰影之類的效果也是直接保存到位圖當中,當需要渲染到屏幕上的時候只需要到緩存中去取對應的位圖進行顯示就行了,加快了整個渲染過程。可以通過勾選instruments core animation中的Color Hits Green and Misses Red選項來查看圖層是否被緩存了,如果圖層顯示為綠色則表示已經(jīng)被緩存起來了,也就是這個緩沖區(qū)的內(nèi)容被復用了,不用在去重新創(chuàng)建緩沖區(qū),反之則是用紅色標示。如下圖可以看到設置shouldRasterize之后,cell都被標示為綠色了,如果滑動過程中發(fā)現(xiàn)都是紅色的證明就有問題了:
?
cached.png
再看看現(xiàn)在滑動的幀率:
優(yōu)化后.png
可以發(fā)現(xiàn)現(xiàn)在滾動的性能大大提高了,光柵化對于那些有很多子view嵌套在一起、view的層級復雜或者有很復雜特效效果的圖層有很明顯的提升,因為這些內(nèi)容都被緩存到位圖當中了。但是使用光柵化需要注意一些內(nèi)容:
?
-
適用于內(nèi)容基本不變的圖層
假如圖層的內(nèi)容經(jīng)常變化,比如cell里面有涉及到動畫之類的,那么緩存的內(nèi)容就無效了,GPU需要重新創(chuàng)建緩存區(qū),導致離屏渲染,這又涉及到OpenGL的上下文環(huán)境切換,反而降低性能。
-
不要過度使用
緩存區(qū)的大小被設置為屏幕大小的2.5倍,假如過分使用同樣會導致大量的離屏渲染。
-
如果緩存的內(nèi)容超過100ms沒有被使用則會被回收。
?
?
tips
?
-
對于圓角可以使用一張中間圓形透明的圖覆蓋在上面,雖然這會引入blending操作,但是大部分情況下性能會比離屏渲染好。
-
讓你的view層次結構平坦一些,因為OpenGL在渲染layer的時候,在碰到有子層級layer的時候可能需要停下來把兩者合成到一個buffer里再接著渲染。(When the OpenGL renderer goes to draw each layer, it may have to stop for some subhierarchies and composite them into a single buffer).
-
延遲加載圖片
有時候在邊滾動邊設置圖片的時候可能會有一定的影響,因此可以在滾動的時候imageview不執(zhí)行setimage的操作,滾動停止的時候才加載圖片,由于滾動的時候NSRunloop是處于UITrackingRunLoopMode模式下,可以采用如下的方式,將設置圖片放到NSDefaultRunLoopMode模式下才進行:
?
UIImage *downloadedImage = ...;
??[self.avatarImageView performSelector:@selector(setImage:)
???????????????????????????? withObject:downloadedImage
???????????????????????????? afterDelay:0
????????????????????????????????inModes:@[NSDefaultRunLoopMode]];
?
-
圖片加載的極限優(yōu)化方式:FastImageCache
https://github.com/path/FastImageCache
?
?
圖形性能這塊有什么好的想法也可提出來交流一下~~
?
參考:
?
-
https://lobste.rs/s/ckm4uw/a_performance-minded_take_on_ios_design/comments/itdkfh
-
Advanced Graphics and Animations for iOS Apps
-
http://iosinjordan.tumblr.com/post/56778173518/help-my-tables-dont-scroll-smoothly
?
?
轉載于:https://www.cnblogs.com/fengmin/p/5937316.html
總結
以上是生活随笔為你收集整理的app 性能优化的那些事(二)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最小公倍数 最大公约数
- 下一篇: 移动端开发问题及技巧汇总