iPhone上面的應用一直都是以流暢的操作體驗而著稱,但是由於之前開發人員把注意力更多的放在開發功能上面,比較少去考慮性能的問題,可能這其中涉及到objective-c,c++跟lua,優化起來相對復雜一些,導致應用在比如touch等較低端的產品上,光從啟動到進入頁面就花了將近一分鍾的時間,頁面之間的切換沒有那種很流暢的感覺,內存也居高不下,比較影響應用的用戶體驗,所以很有必要進行一些優化,下面記錄一下我在優化的過程中的一些心得:
1 instruments
在iOS上進行性能分析的時候,首先考慮借助instruments這個利器分析出問題出在哪,不要憑空想象,不然你可能把精力花在了1%的問題上,最后發現其實啥都沒優化,比如要查看程序哪些部分最耗時,可以使用Time Profiler,要查看內存是否泄漏了,可以使用Leaks等。關於instruments網上有很多資料,作為一個合格iOS開發者,熟悉這個工具還是很有必要的。
2 不要阻塞主線程
在iOS里關於UIKit的操作都是放在主線程,因此如果主線程被阻塞住了,你的UI可能無法及時響應事件,給人一種卡頓的感覺。大多數阻塞主線程的情況是在主線程做IO操作,比如文件的讀寫,包含數據庫、圖片、json文本或者log日志等,盡量將這些操作放放到子線程(如果數據庫有一次有較多的操作,記得采用事務來處理,性能相差還是挺大的),或者在后台建立對應的dispatch queue來做這些操作,比如一個低級別的serial queue來負責log文件的記錄等等。程序中如果你的代碼邏輯是按照同步的邏輯來寫的,盡量修改邏輯代碼吧。。。
3 使用cache
一般為了提升用戶體驗,都會在應用中使用緩存,比如對於圖片資源可以使用SDWebImage這個開源庫,里面就實現了一個圖片緩存的功能。參考SDWebImage的代碼自己也可以實現緩存功能:

業務層根據資源的url向resourcemanager獲取對應的資源,resourcemanager首先會到memorycache這邊去獲取資源,memorycache可以利用NSCache實現,因為NSCache首先是線程安全的,而且在收到內存警告的時候會自己釋放對應的內存;如果memorycache沒有對應的資源再去disk查找,disk也沒有的話再去internet獲取,獲取到的話會更新到memorycache和disk中,具體可以去參考一下SDWebimage的實現細節。
4 減少程序啟動過程中的任務
當用戶點擊app的圖標之后,程序應該盡可能快的進入到主頁面,盡可能減少用戶的等待時間,比如我們的應用程序在啟動的時候會去做3d模型的渲染操作,完成之后在進入首頁面展示,但其實我們可以先進入到主頁面,將渲染3d的任務放到子線程去完成,縮短用戶需要等待的時間。

5 使用合適的數據結構
根據不同的業務場景來選擇合適的數據結構,可能在數據量比較少的時候看不出什么區別,但是假如你存儲的數據量比較大且數據結構比較復雜的話,這有可能會影響到你的程序性能。一般用的比較多的數據結構就是array,但我們知道它的查找復雜度是O(n),因此假如需要快速的查找某個元素,可以使用map。可以參考下Apple Collections Programming Topics 。
6 內存
一般開發都使用的ARC,不太需要開發者去關注內存的創建和釋放這塊,但假如你使用的是MRC,並且跟其它語言混雜在一起(比如c++和lua)等的時候,如何確保內存正確釋放就是你需要考慮的問題了。有時候一些內存泄漏instruments可能無法准確的分析出來,那么就需要自己去排查了,可以使用method swizzling方法來輔助我們排查內存泄漏的問題,確保程序的正確運行。
7 懶加載view
不要在cell里面嵌套太多的view,這會很影響滑動的流暢感,而且更多的view也需要花費更多的CPU跟內存。假如由於view太多而導致了滑動不流暢,那就不要在一次就把所有的view都創建出來,把部分view放到需要顯示cell的時候再去創建。
8 lua優化
由於項目的業務是以及部分框架是用lua語言實現的,因此也順便說一下lua這塊遇到的問題。lua號稱是最快的腳本語言,一般性能上不會有什么問題,如果lua代碼要優化的話,網上也有很多這塊優化的注意點,這次我主要說個可能影響性能的點---lua的垃圾回收。垃圾回收是一個比較耗時的操作,假如垃圾回收的操作太過於頻繁勢必會影響到這個程序的運行,比如在iPod在利用lua_cjson解析一份4.7M的json文件是花了3.43s的時間,后來發現跟垃圾回收這塊有關。一般內存的使用量適中的話,可以不用去理他,讓lua的incremental模式自己去處理,正常情況這個會工作的比較好;假如想要自己去控制gc的運行,可以設置gc的參數,這些參數可能會跟項目有一定的關系,可以自己多試驗取最優值。
//gc 的參數設置,根據情況取最優值 collectgarbage("setpause", 150) collectgarbage("setstepmul", 200)
以上是我在優化過程中的一些記錄總結,關於iOS圖形性能這塊的優化可以查看這里,要是有什么關於性能優化想法也可以提出來~😄(ps:you)
原文鏈接:http://www.jianshu.com/p/5cf9ac335aec
著作權歸作者所有,轉載請聯系作者獲得授權,並標注“簡書作者”。
這次我們來說說iOS app中滑動的那些事。iOS為了提高滑動的流暢感,特意在滑動的時候將runloop模式切換到UITrackingRunLoopMode,在這個過程中專心做跟滑動相關的工作,這也就是在滑動過程中為什么nstimer無法工作的原因,因為兩個沒在同一mode下面。但我們可能經常會遇到滑動不怎么流暢的情況,比如在項目中碰到在滑動tableview的時候不怎么順暢,感覺有點不爽,即便是在測試中表現最好的5s(touch之類的感受更直觀)。

那碰到這種情況該怎么處理,分析圖像動畫性能主要用的是Core Animation這個組件,先簡單介紹一下里面一些經常用到的選項:
- Color Blended layers
標示混合的圖層會為紅色,不透明的圖層為綠色,通常我們希望綠色的區域越多越好。 - 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,這上面的一些分享非常有技術性)

當你碰到性能問題的時候,你可以思考一下:
是否受到CPU或者GPU的限制?
是否有不必要的CPU渲染?
是否有太多的離屏渲染操作?
是否有太多的圖層混合操作?
是否有奇怪的圖片格式或者尺寸?
是否涉及到昂貴的view或者效果?
view的層次結構是否合理?
那么哪些是你最該開始考慮的方向呢?通常發生圖形性能問題的時候,比如列表滑動不順暢、動畫卡頓等,大部分都是由於Offscreen Rendering(離屏渲染)或者blending導致的,因為這在動畫的每一幀都會涉及到。
offscreen-render
什么是offscreen-render?offscreen-render涉及的內容比較多,有offscreen-render那就有onscreen render,onscreen render指的是GPU在當前用於顯示的屏幕緩沖區進行渲染,相反offscreen-render就是不在當前的屏幕緩存區,而在另外的緩沖區進行渲染,offscreen-render有兩種形式:
CPU的offscreen-render
使用CPU來完成渲染操縱,通常在你使用:
- drawRect (如果沒有自定義繪制的任務就不要在子類中寫一個空的drawRect方法,因為只要實現了該方法,就會為視圖分配一個寄宿圖,這個寄宿圖的像素尺寸等於視圖大小乘以 contentsScale的值,造成資源浪費)
- 使用Core Graphics
上面的兩種情況使用的就是CPU離屏渲染,首先分配一塊內存,然后進行渲染操作生成一份bitmap位圖,整個渲染過程會在你的應用中同步的進行,接着再將位圖打包發送到iOS里一個單獨的進程--render server,理想情況下,render server將內容交給GPU直接顯示到屏幕上。
GPU的offscreen-render
使用GPU在當前屏幕緩沖區以外開辟一個新的緩沖區進行繪制,通常發生的情況有:
- 設置cornerRadius, masks, shadows,edge antialiasing等
- 設置layer.shouldRasterize = YES

offscreen-render對性能到底有什么影響?通常大家說的離屏渲染指的是GPU這塊(當然CPU這塊也會有影響,也需要消耗一定的資源),比如修改了layer的陰影或者圓角,GPU需要做額外的渲染操作。通常GPU在做渲染的時候是很快的,但是涉及到offscreen-render的時候情況就可能有些不同,因為需要額外開辟一個新的緩沖區進行渲染,然后繪制到當前屏幕的過程需要做onscreen跟offscreen上下文之間的切換,這個過程的消耗會比較昂貴,涉及到OpenGL的pipeline跟barrier,而且offscreen-render在每一幀都會涉及到,因此處理不當肯定會對性能產生一定的影響,所以可以的話盡量減少offscreen-render的圖層,查看哪些圖層需要離屏渲染可以用Instruments的Core Animation工具進行檢測,Color Offscreen-Rendered Yellow選項會將對應的圖層標記為黃色。
Blending
假如最上層的view是不透明的,那直接使用這個view的對應顏色之就可以,但如果view是透明的,在計算像素的顏色值時就需要計算它下面圖層,透明的視圖越多,計算量就越大,因此也會對圖形的性能產生一定的影響,所以可以的話也盡量減少透明圖層的數目。
Demo
下面給出一個簡單demo的優化過程,這個demo里面涉及到的問題是在實際項目中所碰到的,也就是最上面那張圖里列表滑動不流暢情況---由陰影以及圓角導致的offscreen-render。
整個頁面就是一個簡單的tableview,其中頭像為圓角,一個label有陰影效果,滑動的時候在iPod上幀率只有可憐的28FPS。


其中黃色的區域就是離屏渲染的地方,也就是含有圓角跟陰影的layer。
shadowPath
設置label的陰影效果可以通過:
cell.sign.layer.shadowOffset = CGSizeMake(0, 2); cell.sign.layer.shadowOpacity = 0.5; cell.sign.layer.shadowColor = [UIColor blackColor].CGColor;
但是你可以發現這會導致離屏渲染,一個簡單的不需要離屏渲染的方法就是制定陰影的路徑,也就是設置layer的shadowPath屬性,通過instruments發現陰影的地方沒有黃色了,幀率也提高到了40FPS:
cell.sign.layer.shadowPath = [UIBezierPath bezierPathWithRect:cell.sign.bounds].CGPath;

rasterize
對於圓角這種類似導致的性能問題,最簡單的就是在列表中不要使用圓角,假如要使用圓角的話,一種最快提升性能的方式就是設置layer的shouldRasterize為YES:
cell.layer.shouldRasterize = YES; cell.layer.rasterizationScale = [UIScreen mainScreen].scale;
雖然被Rasterize的圖層也會引起離屏渲染,如下圖所示,整個cell都被標示為黃色:

layer設置shouldRasterize=YES之后,會把被光柵化的圖層保存成位圖並緩存起來,其中圓角或者陰影之類的效果也是直接保存到位圖當中,當需要渲染到屏幕上的時候只需要到緩存中去取對應的位圖進行顯示就行了,加快了整個渲染過程。可以通過勾選instruments core animation中的Color Hits Green and Misses Red選項來查看圖層是否被緩存了,如果圖層顯示為綠色則表示已經被緩存起來了,也就是這個緩沖區的內容被復用了,不用在去重新創建緩沖區,反之則是用紅色標示。如下圖可以看到設置shouldRasterize之后,cell都被標示為綠色了,如果滑動過程中發現都是紅色的證明就有問題了:

再看看現在滑動的幀率:

可以發現現在滾動的性能大大提高了,光柵化對於那些有很多子view嵌套在一起、view的層級復雜或者有很復雜特效效果的圖層有很明顯的提升,因為這些內容都被緩存到位圖當中了。但是使用光柵化需要注意一些內容:
- 適用於內容基本不變的圖層
假如圖層的內容經常變化,比如cell里面有涉及到動畫之類的,那么緩存的內容就無效了,GPU需要重新創建緩存區,導致離屏渲染,這又涉及到OpenGL的上下文環境切換,反而降低性能。 - 不要過度使用
緩存區的大小被設置為屏幕大小的2.5倍,假如過分使用同樣會導致大量的離屏渲染。 - 如果緩存的內容超過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不執行setimage的操作,滾動停止的時候才加載圖片,由於滾動的時候NSRunloop是處於UITrackingRunLoopMode模式下,可以采用如下的方式,將設置圖片放到NSDefaultRunLoopMode模式下才進行:UIImage *downloadedImage = ...; [self.avatarImageView performSelector:@selector(setImage:) withObject:downloadedImage afterDelay:0 inModes:@[NSDefaultRunLoopMode]];
- 圖片加載的極限優化方式: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
原文鏈接:http://www.jianshu.com/p/2a01e5e2141f
著作權歸作者所有,轉載請聯系作者獲得授權,並標注“簡書作者”。
提供了基於Swift3.0模仿的新浪微博的Demo,大家可以下載看一看:基於Swift3.0高仿的微博客戶端,里面針對於微博首頁的復雜頁面的優化做了很多的處理,頁面的FPS 一直保持在59 ~ 60 。
看下demo的效果:

CPU 和GPU
關於繪圖和動畫有兩種處理方式CPU(中央處理器)和GPU(圖形處理器),CPU的工作都在軟件層面,而GPU的在硬件層面。
總的來說,可以使用CPU做任何事情,但是對於圖像的處理,通常GPU會更快,因為GPU使用圖像對高度並行浮點運算做了優化(盡管我也不知道是什么鬼??),所以,我們想盡可能的把屏幕渲染的工作交給硬件去處理,而問題在於GPU並沒有無限制處理的性能,一旦資源用盡,即使CPU並沒有完全占用,GPU性能還是會下降。
所以,目前大多的性能優化都是關於智能利用GPU和CPU,平衡它們之間工作負載。
測量,而不是猜測
現在知道哪些可能會影響性能,該如何修復呢?有許多傳統的詭計來優化,但如果盲目使用的話,可能會造成更多性能上的問題,而不是優化了
如何正確的測量而不是猜測這點很重要,根據性能相關知識寫出的代碼不同於倉促優化,前者是正確的姿勢,后者則是在浪費生命
那改如何測量,第一步就是確保在真實環境下測試你的程序
真機測試,而不是模擬器
當你開始做一些性能方面的工作時候,一定要在真機上測試,而不是模擬器,模擬器雖然可以加快開發效率,但卻不能准確提供真機的性能參數
模擬器運行在Mac上,然而Mac上的cpu比iOS設備要快很多,相反,Mac上的GPU和ios設備上的不一樣,模擬器不得已需要在軟件層面(CPU)模擬ios設備,所以GPU的相關操作在模擬器上面運行的會更慢
另一件重要的就是性能測試的時候一定要用發布配置,而不是調試模式,因為當用發布環境打包的時候,編譯器會引入一些提高性能的優化,比如:去除調試符號或者移除並重新組織代碼,因為可以自己做到這些,比如禁用NSlog、print語句,因為 ,只需要關心發布性能。
測試幀率
可以在程序中使用CADisplayLink來測量幀率,在屏幕上顯示出來,我用Swift3.0模仿YY大神的代碼寫了一個簡單的FPS指示器 YWFPSLabel,使用CADisplayLink監視FPS數值,日常開發的時候,可以有直接的體現,不用再靠猜了...
YWFPSLabel集成也很方便,在AppDelegate的application方法里加下面兩句即可
let FPSLab = YWFPSLabel(frame: CGRect()) UIApplication.shared.keyWindow!.addSubview(FPSLab)
不知道大家有木有看到頭部那個小label啊~~~
但是應用內的FPS顯示並不能完全真實的測量出性能,因為它僅僅能測試出應用內的幀率,還有很多是動畫都是在應用外發生(在渲染進程中處理),但應用內FPS計數可以對一些性能問題提供參考,一旦找到問題,需要更多的精確詳細信息來定位問題所在,我們就要使用Instuments了,它可以看到更多准確是信息,查看到所有與顯示的數據。
Instuments
Instuments是Xcode套件中沒有被充分利用的工具,很多iOS開發者從來沒用過Instrument,特別是通過短暫培訓出來的同學們,所以,很多面試官也會問性能條調優方面的知識,來判斷面試的同學是否真正應用對年開發經驗。
-
Activity Monitor
個人覺的很像Windows的任務管理器,可以查看所有的進程,以及進程的內存、cpu使用百分比等數據等,就不多介紹了,打開一看大概就知道怎么回事
-
Allocations
管理內存是app開發中最重要的一個方面,對於開發者來說,在程序架構中減少內存的使用通常都是使用Allocations去定位和找出減少內存使用的方式
接下來,談一下內存泄漏的兩種情況 - 第一種:為對象A申請了內存空間,之后再也沒用過對象A,也沒釋放過A導致內存泄漏,這種是Leaked Memory內存泄漏
- 第二種:類似於遞歸,不斷地申請內存空間導致的內存泄漏,這種情況是Abandoned Momory
此工具可以讓開發者很好的了解每個方法占用內存的情況,並定位相關的代碼
Allocations查看方法占用內存.png
右鍵就可以打開Xcode自動定位到相關占用內存方法的代碼上

第二種情況可以根據下圖的操作清晰的找到對用的代碼問題

解釋一下,第二種情況我們應該如何操作,重復的執行一系列的操作時候內存不會繼續增加,比如打開和關閉一個窗口,這樣的操作,每一次操作的前后,內存應該是相同的,通過多次循環操作,內存不會遞增下去,通過這種分析結果,觀察內存分配趨勢,當發現不正確的結果或者矛盾的結果,就可以研究是不是Abandoned Momory的問題,並可以修正這個問題了
Core Animation
之前說過自己寫的YWFPSLabel只能檢測應用內的FPS,而此工具考慮到了程序外的動畫,理想的FPS值為60左右,過低的話就用該進性優化了,根據WWDC的說法,當FPS 低於45的時候,用戶就會察覺到到滑動有卡頓

圈着數字紅色方框中的數字,代表着FPS值,理論上60最佳,實際過程中59就可以了,說明就是很流暢的,說明一下操作方式:在手指不離開屏幕的情況下,上下滑動屏幕列表
介紹一下Deug Display中選項的作用
打開此選項屏幕的效果圖如下:

這個選項基於渲染程度對屏幕中的混合區域進行綠到紅的高亮(也就是多個半透明圖層的疊加),由於重繪的原因,混合對GPU性能會有影響,同時也是滑動或者動畫掉幀的罪魁禍首之一
GPU每一幀的繪制的像素有最大限制,這個情況下可以輕易繪制整個屏幕的像素,但如果發生重疊像素的關系需要不停的重繪同一區域的,掉幀和卡頓就有可能發生
GPU會放棄繪制那些完全被其他圖層遮擋的像素,但是要計算出一個圖層是否被遮擋也是相當復雜並且會消耗CPU的資源,同樣,合並不同圖層的透明重疊元素消耗的資源也很大,所以,為了快速處理,一般不要使用透明圖層,
1). 給View添加一個固定、不透明的顏色
2). 設置opaque 屬性為true
但是這對性能調優的幫助並不大,因為UIView的opaque 屬性默認為true,也就是說,只要不是認為設置成透明,都不會出現圖層混合
而對於UIIimageView來說,不僅需要自身需要不是透明的,它的圖片也不能含有alpha通道,這也上圖9張圖片是綠色的原因,因此圖像自身的性質也可能會對結果有影響,所以你確定自己的代碼沒問題,還出現了混合圖層可能就是圖片的問題了
而針對於屏幕中的文字高亮成紅色,是因為一沒有給文字的label增加不透明的背景顏色,而是當UILabel內容為中文時,label的實際渲染區域要大於label的size,因為外圍有了一圈的陰影,才會出現圖層混合我們需要給中文的label加上如下代碼:
retweededTextLab?.layer.masksToBounds = true retweededTextLab?.backgroundColor = UIColor.groupTableViewBackground statusLab.layer.masksToBounds = true statusLab.backgroundColor = UIColor.white
看下效果圖:

那些label的顏色也變成藍色的了,這里有一點需要說明一下,
1). statusLab.layer.masksToBounds = true
單獨使用不會出現離屏渲染
2). 如果對label設置了圓角的話,圓角部分會離屏渲染,離屏渲染的前提是位圖發生了形變
這個選項主要是檢測我們有無濫用或正確使用layer的shouldRasterize屬性.成功被緩存的layer會標注為綠色,沒有成功緩存的會標注為紅色。
很多視圖Layer由於Shadow、Mask和Gradient等原因渲染很高,因此UIKit提供了API用於緩存這些Layer,self.layer.shouldRasterize = true
系統會將這些Layer緩存成Bitmap位圖供渲染使用,如果失效時便丟棄這些Bitmap重新生成。圖層Rasterization柵格化好處是對刷新率影響較小,壞處是刪格化處理后的Bitmap緩存需要占用內存,而且當圖層需要縮放時,要對刪格化后的Bitmap做額外計算。 使用這個選項后時,如果Rasterized的Layer失效,便會標注為紅色,如果有效標注為綠色。當測試的應用頻繁閃現出紅色標注圖層時,表明對圖層做的Rasterization作用不大。
在測試的過程中,第一次加載時,開啟光柵化的layer會顯示為紅色,這是很正常的,因為還沒有緩存成功。但是如果在接下來的測試,。例如我們來回滾動TableView時,我們仍然發現有許多紅色區域,那就需要謹慎對待了
這個選項主要檢查我們有無使用不正確圖片格式,由於手機顯示都是基於像素的,所以當手機要顯示一張圖片的時候,系統會幫我們對圖片進行轉化。比如一個像素占用一個字節,故而RGBA則占用了4個字節,則1920 x 1080的圖片占用了7.9M左右,但是平時jpg或者png的圖片並沒有那么大,因為它們對圖片做了壓縮,但是是可逆的。所以此時,如果圖片的格式不正確,則系統將圖片轉化為像素的時間就有可能變長。而該選項就是檢測圖片的格式是否是系統所支持的,若是GPU不支持的色彩格式的圖片則會標記為青色,則只能由CPU來進行處理。CPU被強制生成了一些圖片,然后發送到渲染服務器,而不是簡單的指向原始圖片的的指針。我們不希望在滾動視圖的時候,CPU實時來進行處理,因為有可能會阻塞主線程。
通常 Core Animation 以每秒10此的頻率更新圖層的調試顏色,對於某些效果來說,這可能太慢了,這個選項可以用來設置每一幀都更新(可能會影響到渲染性能,所以不要一直都設置它)
這里會高亮那些被縮放或者拉伸以及沒有正確對齊到像素邊界的圖片,即圖片Size和imageView中的Size不匹配,會使圖過程片縮放,而縮放會占用CPU,所以在寫代碼的時候保證圖片的大小匹配好imageView,如下圖所示:
圖片尺寸 170 * 220px
let imageView = UIImageView(frame: CGRect(x: 50, y: 100, width: 170, height: 220)) imageView.image = UIImage(named: "cat") view.addSubview(imageView)
蘋果的單位以點計算,而 imageView的尺寸是170 220 pt 而圖片是 170 220px,所以相當於在屏幕上對圖片方法了一倍,看效果圖如下:

可以看到圖片高亮成黃色顯示,更改下imageView的大小
let imageView = UIImageView(frame: CGRect(x: 50, y: 100, width: 85, height: 110)) imageView.image = UIImage(named: "cat") view.addSubview(imageView)
看下效果圖

當imageView和image的大小一致的時候,就正常顯示了
這里會把那些需要離屏渲染的到圖層高亮成黃色,而出發離屏渲染的可能有
/* 圓角處理 */ view.layer.maskToBounds = truesomeView.clipsToBounds = true /* 設置陰影 */ view.shadow.. /* 柵格化 */ view.layer.shouldRastarize = true
針對柵格化處理,我們需要指定屏幕的分辨率
//離屏渲染 - 異步繪制 耗電 self.layer.drawsAsynchronously = true //柵格化 - 異步繪制之后 ,會生成一張獨立的圖片 cell 在屏幕上滾動的時候,本質上滾動的是這張圖片 //cell 優化,要盡量減少圖層的數量,想當於只有一層 //停止滾動之后,可以接受監聽 self.layer.shouldRasterize = true //使用 “柵格化” 必須指定分辨率 self.layer.rasterizationScale = UIScreen.main.scale
指定陰影的路徑,可以防止離屏渲染
// 指定陰影曲線,防止陰影效果帶來的離屏渲染 imageView.layer.shadowPath = UIBezierPath(rect: imageView.bounds).cgPath
這行代碼制定了陰影路徑,如果沒有手動指定,Core Animation會去自動計算,這就會觸發離屏渲染。如果人為指定了陰影路徑,就可以免去計算,從而避免產生離屏渲染。
設置cornerRadius本身並不會導致離屏渲染,但很多時候它還需要配合layer.masksToBounds = true使用。根據之前的總結,設置masksToBounds會導致離屏渲染。解決方案是盡可能在滑動時避免設置圓角,如果必須設置圓角,可以使用光柵化技術將圓角緩存起來:
// 設置圓角 label.layer.masksToBounds = true label.layer.cornerRadius = 8 label.layer.shouldRasterize = true label.layer.rasterizationScale = layer.contentsScale
如果界面中有很多控件需要設置圓角,比如tableView中,當tableView有超過25個圓角,使用如下方法
view.layer.cornerRadius = 10 view.maskToBounds = Yes
那么fps將會下降很多,特別是對某些控件還設置了陰影效果,更會加劇界面的卡頓、掉幀現象,對於不同的控件將采用不同的方法進行處理:
1). 對於label類,可以通過CoreGraphics來畫出一個圓角的label
2). 對於imageView,通過CoreGraphics對繪畫出來的image進行裁邊處理,形成一個圓角的imageView,代碼如下:
/// 創建圓角圖片 /// /// - parameter radius: 圓角的半徑 /// - parameter size: 圖片的尺寸 /// - parameter backColor: 背景顏色 默認 white /// - parameter lineWith: 圓角線寬 默認 1 /// - parameter lineColor: 線顏色 默認 darkGray /// /// - returns: image func yw_drawRectWithRoundCornor(radius: CGFloat, size: CGSize, backColor: UIColor = UIColor.white, lineWith: CGFloat = 1, lineColor: UIColor = UIColor.darkGray) -> UIImage? { let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size) UIGraphicsBeginImageContextWithOptions(rect.size, true, 0) let bezier = UIBezierPath(roundedRect: rect, byRoundingCorners: UIRectCorner.allCorners, cornerRadii: CGSize(width: radius, height: radius)) backColor.setFill() UIRectFill(rect) bezier.addClip() draw(in: rect) bezier.lineWidth = 1 lineColor.setStroke() bezier.stroke() let result = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return result }
這個選項會對任何直接使用OpenGL繪制的圖層進行高亮,如果僅僅使用UIKit或者Core Animation的API,不會有任何效果,恕我才疏學淺,所以,我在測試的時候,確實在屏幕上沒有任何反應的,openGL 繪制,我也不會,所以,就不知道到底會有什么效果了,哪位大神會的話,貼段代碼,給我試試啊~~~
此選項會對重繪的內容進行高亮成黃色,也就是軟件層面使用Core Graphics 繪制的圖層。我測試的時候,好像有點問題,這種解釋,不知道是不是我寫代碼的問題,所以,就不多說了
上面說的這些高亮圖層,幾個常用的選項在模擬器里面可以直接調試,非常方便

紅框中的選項,上面都有解釋,這里就不說了,勾選項,打開模擬器,一看就知道了~
麻蛋、Core Animation 部分終於扯完了,扯了好多啊。。。
又一個灰常重要的工具,主要檢查內存泄漏,在前面Allcations里面我們提到內存泄漏分兩種,現在我們研究Leaked Memory, 從用戶使用角度來看,內存泄漏本身不會產生什么危害,作為用戶,根本感覺不到內存泄漏的存在,真正的危害在於內存泄漏的堆積,最終會耗盡系統所有的內存。我們直接看圖:

界面的介紹
在 instruments 中,雖然選擇了 Leaks 模板,但默認情況下也會添加 Allocations 模板.基本上凡是內存分析都會使用 Allocations 模板, 它可以監控內存分布情況。
選中 Allocations 模板3區域會顯示隨着時間的變化內存使用的折線圖,同時在4區域會顯示內存使用的詳細信息,以及對象分配情況.
點擊 Leaks 模板, 可以查看內存泄露情況。如果在3區域有 紅X 出現, 則有內存泄露, 4區域則會顯示泄露的對象.
打用leaks進行監測:點擊泄露對象可以在(下圖)看到它們的內存地址, 占用字節, 所屬框架和響應方法等信息.打開擴展視圖, 可以看到右邊的跟蹤堆棧信息,4 黑色代碼最有可能出現內存泄漏的方法

監測結果的分析,

Time Profiler
在開發的過程中,我們經常能感覺到,點擊某一按鈕,或者做了某一操作,有卡頓,這就是延遲,那使用此工具,就可以揪出耗時的函數,先看一下,調試界面介紹:

根據查看的相關耗時操作,我們就可以右鍵定位當耗時的方法:
寫一個簡單例子看一下:

看上圖,可以很清楚看到此方法耗時比較嚴重,右鍵打開定位到此方法的代碼:

代碼截圖如下:

這時候,我們把循環放到子線程來做
@IBAction func btnAction(_ sender: AnyObject) { let svc = SecondViewController() svc.title = "第二個頁面" //全局隊列異步執行 DispatchQueue.global().async { for i in 0..<8888 { print(i) } } navigationController?.pushViewController(svc, animated: true) }
看效果圖:

到這里比較重要Instrument調試工具介紹的差不多了,說一個Xcode8.0新出的功能,很好用也很重要的功能:

還是以例子說說吧,Viewcontroller里面一個button,點擊跳到SecondViewcontroller,SecondViewcontroller里面有個View,view里面有個button,button點擊回到ViewController,實現是通過view的屬性拿到SecondviewConroller的引用,pop回去
子view的代碼如下:
class SubView: UIView { var delegate: SecondViewController? @IBAction func brnAction(_ sender: AnyObject) { delegate?.navigationController!.popViewController(animated: true) } }
當我們從第二個控制器,回到第一個控制器的時候,我們點一下,剛那個按鈕,看圖:

第二個控制器和子View都內存中,我們很容易,就可以發現問題了,這是因為,SecondViewController強引用了SubView,而我們SubView也強引用了SecondViewcontroller,就造成相互強引用,引用計數器不能為0,不能銷毀了,我們只要把屬性前面加個weak,變成弱引用就可以了
weak var delegate: SecondViewController? @IBAction func brnAction(_ sender: AnyObject) { delegate?.navigationController!.popViewController(animated: true) }
這時候,我們從第二個控制器pop回來的時候,看下內存:

現在就沒問題了,怎樣這個工具是不是挺好用啊
還有一些針對TableView,collectionView的優化,有空再聊吧,
- 聲明:
1.本文有些文字描述,來自於其他博客和文章,因為覺的描述挺好(好吧我承認、自己也寫不出來),所以就拿來用了,若有不妥的地方,請告知我,我會立即刪除修改
2.因為本人的水平有限,可能有寫的不對的地方,歡迎各位大大多指點、不勝感激
最后的最后:碼字不易,如果對你有用,點個喜歡唄!
原文鏈接:http://www.jianshu.com/p/05b68c84913a
著作權歸作者所有,轉載請聯系作者獲得授權,並標注“簡書作者”。