iOS App性能優(yōu)化
29 MAY 2013 on iOS, Performance
iOS App的性能關(guān)注點(diǎn)
雖然iPhone的機(jī)能越來越好相满,但是app的功能也越來越復(fù)雜捌显,性能從來都是移動(dòng)開發(fā)的核心關(guān)注點(diǎn)之一。我們說一個(gè)app性能好字管,不是簡單指感覺運(yùn)行速度快,而應(yīng)該是指應(yīng)用啟動(dòng)快速衷敌、UI反饋響應(yīng)及時(shí)鳖悠、列表滾動(dòng)操作流暢、內(nèi)存使用合理溶锭,當(dāng)然更不能隨隨便便Crash啦宝恶。工程師開發(fā)應(yīng)用時(shí)除了在設(shè)計(jì)上要避免性能“坑”的出現(xiàn),在實(shí)際遇到“坑”時(shí)也要能很快定位原因所在趴捅。定位性能問題原因當(dāng)然不能靠猜垫毙,合理的方法是使用工具測量評估出投資回報(bào)最高的問題點(diǎn),然后再加以優(yōu)化拱绑。
本文會(huì)從以下幾點(diǎn)介紹如何分析和優(yōu)化iOS app的性能:啟動(dòng)時(shí)間综芥、用戶響應(yīng)、內(nèi)存猎拨、圖形動(dòng)畫膀藐、文件和網(wǎng)絡(luò)I/O。其中會(huì)用到Apple出品的性能分析神器“Instruments”红省。
啟動(dòng)時(shí)間
應(yīng)用啟動(dòng)時(shí)間長短對用戶第一次體驗(yàn)至關(guān)重要额各,同時(shí)系統(tǒng)對應(yīng)用的啟動(dòng)、恢復(fù)等狀態(tài)的運(yùn)行時(shí)間也有嚴(yán)格的要求类腮,在應(yīng)用超時(shí)的情況下系統(tǒng)會(huì)直接關(guān)閉應(yīng)用。以下是幾個(gè)常見場景下系統(tǒng)對app運(yùn)行時(shí)間的要求:
Launch 20秒
Resume 10秒
Suspend 10秒
Quit 6秒
Background Task 10分鐘
要獲取準(zhǔn)確的app啟動(dòng)所需時(shí)間蛉加,最簡單的方法時(shí)首先在main.c中添加如下代碼:
CFAbsoluteTime StartTime;
int main(int argc, char **argv) {
StartTime = CFAbsoluteTimeGetCurrent();
然后在AppDelegate的回調(diào)方法application:didFinishLaunchingWithOptions中添加:
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@”Lauched in %f seconds.”,? (CFAbsoluteTimeGetCurrent() – StartTime));
});
可能你會(huì)覺得為什么這樣可拿到系統(tǒng)啟動(dòng)的時(shí)間蚜枢,因?yàn)檫@個(gè)dispatch_async中提交的工作會(huì)在app主線程啟動(dòng)后的下一個(gè)run lopp中運(yùn)行,此時(shí)app已經(jīng)完成了載入并且將要顯示第一幀畫面针饥,也就是系統(tǒng)會(huì)運(yùn)行到-[UIApplication _reportAppLaunchFinished]之前厂抽。下圖是用Instruments工具Time Profiler跑的調(diào)用棧,Instruments的使用方法建議看WWDC中與performance相關(guān)的session錄像丁眼,文字寫起來太單薄不夠直觀哈筷凤。
從圖中我們可以看到在系統(tǒng)調(diào)用[UIApplication _reportAppLaunchFinished]之前完成了系統(tǒng)回調(diào)application:didFinishLaunchingWithOptions。
App的啟動(dòng)會(huì)包括以下幾個(gè)部分(來自WWDC 2012 Session 235):
1)鏈接和載入:可以在Time Profile中顯示dyld載入庫函數(shù),庫會(huì)被映射到地址空間藐守,同時(shí)完成綁定以及靜態(tài)初始化挪丢。
2)UIKit初始化:如果應(yīng)用的Root View Controller是由XIB實(shí)現(xiàn)的,也會(huì)在啟動(dòng)時(shí)被初始化卢厂。
3)應(yīng)用回調(diào):調(diào)用UIApplicationDeleagte的回調(diào):application:didFinishLaunchingWithOptions
4)第一次Core Animation調(diào)用:在啟動(dòng)后的方法-[UIApplication _resportAppLaunchFinished]中調(diào)用CA::Transaction::commit實(shí)現(xiàn)第一幀畫面的繪制乾蓬。
如果你的程序啟動(dòng)很慢,能 做的首先是將與顯示第一屏畫面無關(guān)的操作放到之后執(zhí)行慎恒;如果是用XIB文件load第一屏任内,XIB文件中的View層也要如果扁平,不要有太多圖層融柬。
用戶響應(yīng)
如何能夠讓用戶覺得你的app響應(yīng)迅速呢死嗦?當(dāng)然是app用戶所觸發(fā)的操作都能得到立刻響應(yīng),即用戶事件(User Event)能夠被主線程的run loop及時(shí)處理粒氧。什么是run loop越除?可以想象成一個(gè)處理事件的select多路復(fù)用。主線程中的run loop當(dāng)然主要是為了處理用戶產(chǎn)生的事件啦靠欢,例如點(diǎn)擊廊敌、滾動(dòng)等。以后我們會(huì)詳細(xì)聊聊run loop這個(gè)讓人迷惑的東東门怪。
要讓主線程的run loop更好的響應(yīng)用戶事件骡澈,工程師應(yīng)該盡量減少主線程干重活的時(shí)間,尤其是讀文件啊掷空,網(wǎng)絡(luò)操作啊肋殴,大量運(yùn)算啊這類重活,如果是阻塞操作坦弟,那就更是大忌了护锤。我們可以用多線程(NSThread、NSOperationQueue, GCD酿傍,下一篇Blog就會(huì)聊到這多線程)將重活移出主線程烙懦,這屬于顯式并發(fā)。還有種隱式并發(fā)赤炒,例如view和layer的動(dòng)畫氯析、layer的繪制以及PNG圖片的解碼都是在另一個(gè)子線程中執(zhí)行的。除了使用多線程技術(shù)減輕主線程的負(fù)擔(dān)外莺褒,減少主線程中阻塞也是提升用戶體驗(yàn)的一個(gè)方法掩缓。使用Instruments中Time Profiler工具中的"Recod thread waiting"選項(xiàng)可以統(tǒng)計(jì)出app運(yùn)行時(shí)各個(gè)線程中的阻塞系統(tǒng)調(diào)用情況,例如文件讀寫read/write遵岩,網(wǎng)絡(luò)讀寫send/recv你辣,加鎖psynchmutexwait等。Instruments中的System Trace工具則能夠記錄所有的底層系統(tǒng)調(diào)用。
內(nèi)存
內(nèi)存問題從來都是iOS app的老大難問題舍哄,搞不好程序就爆了宴凉。由于iOS系統(tǒng)沒有Swap文件(知道為啥不?留給懸念)蠢熄,在內(nèi)存不足時(shí)會(huì)將只讀數(shù)據(jù)(例如code page)從內(nèi)存中移出跪解,需要的時(shí)候再從disk上讀如內(nèi)存;可讀寫數(shù)據(jù)不會(huì)被系統(tǒng)從內(nèi)存中移出签孔,然而如果占用的內(nèi)存達(dá)到一個(gè)閾值叉讥,系統(tǒng)會(huì)發(fā)出相應(yīng)的通知和回調(diào)讓應(yīng)用release對象以回收內(nèi)存,如果仍然不能減少內(nèi)存使用量饥追,系統(tǒng)會(huì)直接關(guān)閉應(yīng)用图仓。尤其是iOS 5.0之后,如果你的app收到了memory warning但绕,那么腦袋也是和其他app一樣放在了案板上救崔,隨時(shí)有可能被kill掉,并不是說一定會(huì)先Kill掉在后臺的app捏顺。
App使用的內(nèi)存除了我們在堆上分配的內(nèi)存外(+[NSobject alloc]/malloc)六孵,還會(huì)有更多使用內(nèi)存的地方,比如代碼和全局?jǐn)?shù)據(jù)(TEXT和DATA)幅骄,線程棧劫窒,圖片,view 的layer backing store等等拆座。因此處理內(nèi)存問題主巍,絕不僅僅是我們開發(fā)app時(shí)盡量少申請內(nèi)存那么簡單。
現(xiàn)在有了超炫的ARC挪凑,內(nèi)存問題相對少了很多孕索,開發(fā)效率也得到了提高。但是很多公司的項(xiàng)目仍然由于歷史原因采用了手動(dòng)管理內(nèi)存躏碳,該做的活還是少不了搞旭。Xcode自帶的靜態(tài)分析功能可以幫你提前發(fā)現(xiàn)一些問題,然而有些內(nèi)存問題是無法用靜態(tài)分析來發(fā)現(xiàn)的菇绵,例如我們不斷使用內(nèi)存沒有及時(shí)釋放的問題肄渗,就無法使用靜態(tài)分析器分析出來。此時(shí)可以使用Instruments的Allocations和Leaks工具來檢查運(yùn)行時(shí)的的內(nèi)存使用以及泄露問題脸甘。
Allocations工具可以很直觀的反應(yīng)app的內(nèi)存使用情況恳啥,還有個(gè)很贊“Mark Heap”功能偏灿,在上圖左邊下半部分中的Heapshot Analysis中丹诀。例如你在進(jìn)入一個(gè)頁面前點(diǎn)擊一下“Mark Heap”,然后再退回上一頁面點(diǎn)擊一下“Mark Heap”,如果你在進(jìn)出這個(gè)頁面里所申請的內(nèi)存都得到了合理的釋放铆遭,那么堆的內(nèi)存增長量就應(yīng)該降至0(見上圖右下部分)硝桩。
另一種嚴(yán)重的內(nèi)存使用問題是引用了已經(jīng)釋放的內(nèi)存,直接導(dǎo)致應(yīng)用崩潰枚荣,而Allocation有一個(gè)選項(xiàng)Enable NSZombie detection能夠在應(yīng)用使用已經(jīng)釋放的內(nèi)存時(shí)標(biāo)注出來碗脊,同時(shí)顯示錯(cuò)誤發(fā)生的調(diào)用棧信息。這為解決問題提供了最直接的幫助橄妆,當(dāng)然缺點(diǎn)是必須能夠重現(xiàn)EXECBADACCESS錯(cuò)誤衙伶。
工具Leaks可以在應(yīng)用運(yùn)行時(shí)直接標(biāo)示出存在內(nèi)存泄露的代碼,如果發(fā)生了內(nèi)存泄露害碾,可以從泄露詳細(xì)信息中查看泄露的具體對象以及方法調(diào)用棧矢劲,大部分問題還是很好解決的。
圖形和動(dòng)畫
圖形性能對用戶體驗(yàn)有直接的影響慌随,Instruments中的Core Animation工具用于測量物理機(jī)上的圖形性能芬沉,通過視圖的刷新頻率大小來判斷應(yīng)用的圖形性能。例如一個(gè)復(fù)雜的列表滾動(dòng)時(shí)它的刷新率應(yīng)該努力趨近于60fps才能讓用戶覺得夠流暢阁猜,從這個(gè)數(shù)字也可以算出run loop最長的響應(yīng)時(shí)間應(yīng)該是16毫秒丸逸。
啟動(dòng)Instruments的Core Animation工具后可以發(fā)現(xiàn)左下部分有一堆選項(xiàng),我們來逐個(gè)介紹:
1) Color Blended Layers
Instruments可以在物理機(jī)上顯示出被混合的圖層Blended Layer(用紅色標(biāo)注)剃袍,Blended Layer是因?yàn)檫@些Layer是透明的(Transparent)黄刚,系統(tǒng)在渲染這些view時(shí)需要將該view和下層view混合(Blend)后才能計(jì)算出該像素點(diǎn)的實(shí)際顏色,如果這種blended layer很多笛园,那么在滾動(dòng)列表時(shí)就甭想有流暢的效果隘击。
解決blended layer問題也很簡單,檢查紅色區(qū)域view的opaque屬性研铆,記得設(shè)置成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],系統(tǒng)會(huì)將這些Layer緩存成Bitmap位圖供渲染使用交煞,如果失效時(shí)便丟棄這些Bitmap重新生成咏窿。圖層Rasterization柵格化好處是對刷新率影響較小,壞處是刪格化處理后的Bitmap緩存需要占用內(nèi)存素征,而且當(dāng)圖層需要縮放時(shí)集嵌,要對刪格化后的Bitmap做額外計(jì)算萝挤。
使用這個(gè)選項(xiàng)后時(shí),如果Rasterized的Layer失效根欧,便會(huì)標(biāo)注為紅色怜珍,如果有效標(biāo)注為綠色。當(dāng)測試的應(yīng)用頻繁閃現(xiàn)出紅色標(biāo)注圖層時(shí)凤粗,表明對圖層做的Rasterization作用不大酥泛。
3) Color Misaligned Images
Misaligned Image表示要繪制的點(diǎn)無法直接映射到頻幕上的像素點(diǎn),此時(shí)系統(tǒng)需要對相鄰的像素點(diǎn)做anti-aliasing反鋸齒計(jì)算嫌拣,增加了圖形負(fù)擔(dān)柔袁,通常這種問題出在對某些View的Frame重新計(jì)算和設(shè)置時(shí)產(chǎn)生的。
上圖中被標(biāo)注為黃色的圖層异逐,這是由于圖層顯示的是被縮放后的圖片瘦馍,如果這些圖片是通過網(wǎng)絡(luò)下載的,可以通過程序更新為確定的繪制大小來解決应役。還有些系統(tǒng)Navigation Bar和Tool Bar的背景圖片使用的是拉伸(Streched)圖片情组,也會(huì)被表示為黃色,這是屬于正常情況箩祥,通常無需修改院崇。這種問題一般對性能影響不大,而是可能會(huì)在邊緣處虛化袍祖。
(4) Color Offscreen-Rendered Yellow
Offscreen-Rendering離屏渲染意思是iOS要顯示一個(gè)視圖時(shí)底瓣,需要先在后臺用CPU計(jì)算出視圖的Bitmap,再交給GPU做Onscreen-Rendering顯示在屏幕上蕉陋,因?yàn)轱@示一個(gè)視圖需要兩次計(jì)算捐凭,所以這種Offscreen-Rendering會(huì)導(dǎo)致app的圖形性能下降。
大部分Offscreen-Rendering都是和視圖Layer的Shadow和Mask相關(guān)凳鬓,下列情況會(huì)導(dǎo)致視圖的Offscreen-Rendering:
使用Core Graphics (CG開頭的類)茁肠。
使用drawRect()方法,即使為空缩举。
將CALayer的屬性shouldRasterize設(shè)置為YES垦梆。
使用了CALayer的setMasksToBounds(masks)和setShadow*(shadow)方法。
在屏幕上直接顯示文字仅孩,包括Core Text托猩。
設(shè)置UIViewGroupOpacity。
這篇博文Designing for iOS: Graphics & Performance對offsreen以及圖形性能有個(gè)很棒的介紹辽慕,
(5) Color Copied Images
Copied Image選項(xiàng)可以標(biāo)注應(yīng)用繪制時(shí)被Core Animation復(fù)制的圖片京腥,標(biāo)注成藍(lán)綠色。雖然我在運(yùn)行時(shí)遇到過溅蛉,不過個(gè)人感覺對圖形性能影響不大公浪。
(6) Color Immediately摆尝,F(xiàn)lash Updated Regions, Color OpenGL Fast Path Blue
Color Immediately選項(xiàng)表示Instruments在做color-flush操作時(shí)取消10毫秒的延時(shí)因悲。
Flash Updated Regions選項(xiàng)用于用紅色示標(biāo)示出在屏幕上使用GPU計(jì)算繪制的圖層。
Color OpenGL Fast Path Blue選項(xiàng)用于用藍(lán)色標(biāo)示出在屏幕上由OpenGL compositor繪制的內(nèi)容勺爱。
這三個(gè)選項(xiàng)對圖形性能的分析意義較小晃琳,通常僅作為參考。
文件和網(wǎng)絡(luò)I/O
如果需要對app的文件和網(wǎng)絡(luò)I/O情況做分析琐鲁,可以用到這三個(gè)Instruments工具System Usage卫旱、File Activity和Network。
工具System Usage可以統(tǒng)計(jì)出運(yùn)行狀態(tài)下應(yīng)用的文件和網(wǎng)絡(luò)IO操作數(shù)據(jù)围段。例如我們發(fā)現(xiàn)應(yīng)用啟動(dòng)后又一個(gè)峰值顾翼,這可能存在問題,我們可以利用System Usage工具的詳細(xì)信息欄查看應(yīng)用是由于對哪些文件的讀寫操作導(dǎo)致了峰值奈泪。
工具File Activity只能在模擬器中運(yùn)行适贸,因此數(shù)據(jù)采集可能不是非常準(zhǔn)確。它同樣可以詳細(xì)給出讀取的文件屬性涝桅、大小拜姿、載入時(shí)間等信息,適合與System Usage配合使用冯遂。
Network工具則可以采集到應(yīng)用的TCP/IP和UDP的使用信息(傳輸?shù)臄?shù)據(jù)量蕊肥、當(dāng)前所有TCP連接等),用得不多蛤肌,做網(wǎng)絡(luò)使用狀況分析時(shí)用用還行壁却。
更多閱讀
涉及iOS App性能的知識很多,上面只是冰山一角裸准,重點(diǎn)推薦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
還有幾篇不錯(cuò)的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/
comments powered by Disqus