iOS App性能優化
iOS App的性能關注點
雖然iPhone的機能越來越好,但是app的功能也越來越復雜,性能從來都是移動開發的核心關注點之一。我們說一個app性能好,不是簡單指 感覺運行速度快,而應該是指應用啟動快速、UI反饋響應及時、列表滾動操作流暢、內存使用合理,當然更不能隨隨便便Crash啦。工程師開發應用時除了在 設計上要避免性能“坑”的出現,在實際遇到“坑”時也要能很快定位原因所在。定位性能問題原因當然不能靠猜,合理的方法是使用工具測量評估出投資回報最高 的問題點,然后再加以優化。
本文會從以下幾點介紹如何分析和優化iOS app的性能:啟動時間、用戶響應、內存、圖形動畫、文件和網絡I/O。其中會用到Apple出品的性能分析神器“Instruments”。
啟動時間
應用啟動時間長短對用戶第一次體驗至關重要,同時系統對應用的啟動、恢復等狀態的運行時間也有嚴格的要求,在應用超時的情況下系統會直接關閉應用。以下是幾個常見場景下系統對app運行時間的要求: * Launch 20秒 Resume 10秒 Suspend 10秒 Quit 6秒 Background Task 10分鐘
要獲取準確的app啟動所需時間,最簡單的方法時首先在main.c中添加如下代碼:
CFAbsoluteTime StartTime; int main(int argc, char **argv) { StartTime = CFAbsoluteTimeGetCurrent();
然后在AppDelegate的回調方法application:didFinishLaunchingWithOptions中添加:
dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@”Lauched in %f seconds.”, (CFAbsoluteTimeGetCurrent() – StartTime)); });可能你會覺得為什么這樣可拿到系統啟動的時間,因為這個dispatch_async中提交的工作會在app主線程啟動后的下一個run lopp中運行,此時app已經完成了載入并且將要顯示第一幀畫面,也就是系統會運行到`-[UIApplication _reportAppLaunchFinished]`之前。下圖是用Instruments工具Time Profiler跑的調用棧,Instruments的使用方法建議看WWDC中與performance相關的[session錄像](https://developer.apple.com/videos/wwdc),文字寫起來太單薄不夠直觀哈。
從圖中我們可以看到在系統調用[UIApplication _reportAppLaunchFinished]之前完成了系統回調application:didFinishLaunchingWithOptions。
App的啟動會包括以下幾個部分(來自 WWDC 2012 Session 235):
1)鏈接和載入:可以在Time Profile中顯示dyld載入庫函數,庫會被映射到地址空間,同時完成綁定以及靜態初始化。
2)UIKit初始化:如果應用的Root View Controller是由XIB實現的,也會在啟動時被初始化。
3)應用回調:調用UIApplicationDeleagte的回調:application:didFinishLaunchingWithOptions
4)第一次Core Animation調用:在啟動后的方法-[UIApplication _resportAppLaunchFinished]中調用CA::Transaction::commit實現第一幀畫面的繪制。如果你的程序啟動很慢,能 做的首先是將與顯示第一屏畫面無關的操作放到之后執行;如果是用XIB文件load第一屏,XIB文件中的View層也要如果扁平,不要有太多圖層。
用戶響應
如何能夠讓用戶覺得你的app響應迅速呢?當然是app用戶所觸發的操作都能得到立刻響應,即用戶事件(User Event)能夠被主線程的run loop及時處理。什么是run loop?可以想象成一個處理事件的select多路復用。主線程中的run loop當然主要是為了處理用戶產生的事件啦,例如點擊、滾動等。以后我們會詳細聊聊run loop這個讓人迷惑的東東。
要讓主線程的run loop更好的響應用戶事件,工程師應該盡量減少主線程干重活的時間,尤其是讀文件啊,網絡操作啊,大量運算啊這類重活,如果是阻塞操作,那就更是大忌 了。我們可以用多線程(NSThread、NSOperationQueue, GCD,下一篇Blog就會聊到這多線程)將重活移出主線程,這屬于顯式并發。還有種隱式并發,例如view和layer的動畫、layer的繪制以及 PNG圖片的解碼都是在另一個子線程中執行的。除了使用多線程技術減輕主線程的負擔外,減少主線程中阻塞也是提升用戶體驗的一個方法。使用 Instruments中Time Profiler工具中的”Recod thread waiting”選項可以統計出app運行時各個線程中的阻塞系統調用情況,例如文件讀寫read/write,網絡讀寫send/recv,加鎖 psynch_mutex_wait等。Instruments中的System Trace工具則能夠記錄所有的底層系統調用。
內存
內存問題從來都是iOS app的老大難問題,搞不好程序就爆了。由于iOS系統沒有Swap文件(知道為啥不?留給懸念),在內存不足時會將只讀數據(例如code page)從內存中移出,需要的時候再從disk上讀如內存;可讀寫數據不會被系統從內存中移出,然而如果占用的內存達到一個閾值,系統會發出相應的通知 和回調讓應用release對象以回收內存,如果仍然不能減少內存使用量,系統會直接關閉應用。尤其是iOS 5.0之后,如果你的app收到了memory warning,那么腦袋也是和其他app一樣放在了案板上,隨時有可能被kill掉,并不是說一定會先Kill掉在后臺的app。
App使用的內存除了我們在堆上分配的內存外(+[NSobject alloc]/malloc),還會有更多使用內存的地方,比如代碼和全局數據( TEXT和DATA),線程棧,圖片,view 的layer backing store等等。因此處理內存問題,絕不僅僅是我們開發app時盡量少申請內存那么簡單。
現在有了超炫的ARC,內存問題相對少了很多,開發效率也得到了提高。但是很多公司的項目仍然由于歷史原因采用了手動管理內存,該做的活還是少 不了。Xcode自帶的靜態分析功能可以幫你提前發現一些問題,然而有些內存問題是無法用靜態分析來發現的,例如我們不斷使用內存沒有及時釋放的問題,就 無法使用靜態分析器分析出來。此時可以使用Instruments的Allocations和Leaks工具來檢查運行時的的內存使用以及泄露問題。
Allocations工具可以很直觀的反應app的內存使用情況,還有個很贊“Mark Heap”功能,在上圖左邊下半部分中的Heapshot Analysis中。例如你在進入一個頁面前點擊一下“Mark Heap”,然后再退回上一頁面點擊一下“Mark Heap”,如果你在進出這個頁面里所申請的內存都得到了合理的釋放,那么堆的內存增長量就應該降至0(見上圖右下部分)。
另一種嚴重的內存使用問題是引用了已經釋放的內存,直接導致應用崩潰,而Allocation有一個選項Enable NSZombie detection能夠在應用使用已經釋放的內存時標注出來,同時顯示錯誤發生的調用棧信息。這為解決問題提供了最直接的幫助,當然缺點是必須能夠重現 EXEC_BAD_ACCESS錯誤。
工具Leaks可以在應用運行時直接標示出存在內存泄露的代碼,如果發生了內存泄露,可以從泄露詳細信息中查看泄露的具體對象以及方法調用棧,大部分問題還是很好解決的。
圖形和動畫
圖形性能對用戶體驗有直接的影響,Instruments中的Core Animation工具用于測量物理機上的圖形性能,通過視圖的刷新頻率大小來判斷應用的圖形性能。例如一個復雜的列表滾動時它的刷新率應該努力趨近于 60fps才能讓用戶覺得夠流暢,從這個數字也可以算出run loop最長的響應時間應該是16毫秒。
啟動Instruments的Core Animation工具后可以發現左下部分有一堆選項,我們來逐個介紹:
1) Color Blended Layers
Instruments可以在物理機上顯示出被混合的圖層Blended Layer(用紅色標注),Blended Layer是因為這些Layer是透明的(Transparent),系統在渲染這些view時需要將該view和下層view混合(Blend)后才能 計算出該像素點的實際顏色,如果這種blended layer很多,那么在滾動列表時就甭想有流暢的效果。
解決blended layer問題也很簡單,檢查紅色區域view的opaque屬性,記得設置成YES;檢查backgroundColor屬性是不是[UIColor clearColor],要知道背景顏色為clear color那可是圖形性能的大敵,基本意味著blended layer是跑不了的了,為什么?自己思考一下:)
2) Color Hits Green and Misses Red
很多視圖Layer由于Shadow、Mask和Gradient等原因渲染很高,因此UIKit提供了API用于緩存這些Layer: [layer setShouldRasterize:YES],系統會將這些Layer緩存成Bitmap位圖供渲染使用,如果失效時便丟棄這些Bitmap重新生 成。圖層Rasterization柵格化好處是對刷新率影響較小,壞處是刪格化處理后的Bitmap緩存需要占用內存,而且當圖層需要縮放時,要對刪格 化后的Bitmap做額外計算。 使用這個選項后時,如果Rasterized的Layer失效,便會標注為紅色,如果有效標注為綠色。當測試的應用頻繁閃現出紅色標注圖層時,表明對圖層 做的Rasterization作用不大。
3) Color Misaligned Images
Misaligned Image表示要繪制的點無法直接映射到頻幕上的像素點,此時系統需要對相鄰的像素點做anti-aliasing反鋸齒計算,增加了圖形負擔,通常這種問題出在對某些View的Frame重新計算和設置時產生的。
上圖中被標注為黃色的圖層,這是由于圖層顯示的是被縮放后的圖片,如果這些圖片是通過網絡下載的,可以通過程序更新為確定的繪制大小來解決。還 有些系統Navigation Bar和Tool Bar的背景圖片使用的是拉伸(Streched)圖片,也會被表示為黃色,這是屬于正常情況,通常無需修改。這種問題一般對性能影響不大,而是可能會在 邊緣處虛化。
(4) Color Offscreen-Rendered Yellow
Offscreen-Rendering離屏渲染意思是iOS要顯示一個視圖時,需要先在后臺用CPU計算出視圖的Bitmap,再交給GPU 做Onscreen-Rendering顯示在屏幕上,因為顯示一個視圖需要兩次計算,所以這種Offscreen-Rendering會導致app的圖 形性能下降。
大部分Offscreen-Rendering都是和視圖Layer的Shadow和Mask相關,下列情況會導致視圖的Offscreen- Rendering: 1. 使用Core Graphics (CG開頭的類)。 2. 使用drawRect()方法,即使為空。 3. 將CALayer的屬性shouldRasterize設置為YES。 4. 使用了CALayer的setMasksToBounds(masks)和setShadow*(shadow)方法。 5. 在屏幕上直接顯示文字,包括Core Text。 6. 設置UIViewGroupOpacity。
這篇博文 Designing for iOS: Graphics & Performance對 offsreen以及圖形性能有個很棒的介紹,(5) Color Copied Images Copied Image選項可以標注應用繪制時被Core Animation復制的圖片,標注成藍綠色。雖然我在運行時遇到過,不過個人感覺對圖形性能影響不大。 (6) Color Immediately,Flash Updated Regions, Color OpenGL Fast Path Blue Color Immediately選項表示Instruments在做color-flush操作時取消10毫秒的延時。Flash Updated Regions選項用于用紅色示標示出在屏幕上使用GPU計算繪制的圖層。Color OpenGL Fast Path Blue選項用于用藍色標示出在屏幕上由OpenGL compositor繪制的內容。 這三個選項對圖形性能的分析意義較小,通常僅作為參考。
文件和網絡I/O
如果需要對app的文件和網絡I/O情況做分析,可以用到這三個Instruments工具System Usage、File Activity和Network。
工具System Usage可以統計出運行狀態下應用的文件和網絡IO操作數據。例如我們發現應用啟動后又一個峰值,這可能存在問題,我們可以利用System Usage工具的詳細信息欄查看應用是由于對哪些文件的讀寫操作導致了峰值。
工具File Activity只能在模擬器中運行,因此數據采集可能不是非常準確。它同樣可以詳細給出讀取的文件屬性、大小、載入時間等信息,適合與System Usage配合使用。
Network工具則可以采集到應用的TCP/IP和UDP的使用信息(傳輸的數據量、當前所有TCP連接等),用得不多,做網絡使用狀況分析時用用還行。
更多閱讀
涉及iOS App性能的知識很多,上面只是冰山一角,重點推薦WWDC的session。
WWDC 2012:
- 406: Adopting Automatic Reference Counting
- 238: iOS App Performance: Graphics and Animations
- 242: iOS App Performance: Memory
- 235: iOS App Performance: Responsiveness
- 409: Learning Instruments
- 706: Networking Best Practices
- 514: OpenGL ES Tools and Techniques
- 506: Optimizing 2D Graphics and Animation Performance
- 601: Optimizing Web Content in UIWebViews and Websites on iOS
- 225: Up and Running: Making a Great Impression with Every Launch
WWDC 2011:
- 105: Polishing Your App: Tips and tricks to improve the responsiveness and performance
- 121: Understanding UIKit Rendering
- 131 performance optimization on iphone os
- 308: Blocks and Grand Central Dispatch in Practice
- 323: Introducing Automatic Reference Counting
- 312: iOS Performance and Power Optimization with Instruments
還有幾篇不錯的blog:
http://oleb.net/blog/2011/11/ios5-tech-talk-michael-jurewitz-on-performance-measurement/
http://eng.pulse.me/tips-for-improving-performance-of-your-ios-application/
http://robots.thoughtbot.com/post/36591648724/designing-for-ios-graphics-performance
http://www.touchwonders.com/en/how-to-make-your-apps-feel-responsive-and-fast-part-2/
出處:http://www.iteye.com