你的 iOS 應用,運行速度靠譜嗎?中槍的同窗莫要愁,性能優化咱有妙招。用 Xcode 自家的調試工具 Instruments,揪出那些堵線程、佔內存、耗資源的問題代碼,完全破掉迷局,讓應用揚眉吐氣!html
對於每位 iOS 開發者來講,代碼性能是個避不開的話題。隨着項目的擴大和功能的增多,沒通過認真調試和優化的代碼,要麼任性地卡頓運行,要麼低調地崩潰了之……結果呢,你們用着不高興,開發者也不開心。ios
其實要破這個局面並不難,只要在 Xcode 自帶的監控調試工具 Instruments 上花點功夫,讓大代碼流暢運行也不是神話。Instruments 提供了不少功能,我會重點介紹一下我最經常使用的三大類:git
即便有 ARC(自動引用計數)內存管理機制,但在現實中對象之間引用複雜,循環引用致使的內存泄漏仍然難以免,因此關鍵時刻還要自力更生。github
針對這三方面的測試,我寫了個演示應用,放在 GitHub 上,來幫助你們更直觀地瞭解這些工具的使用方法。好,進入正題。算法
時間都去哪兒啦? Time Profiler 能夠回答。它會按照設定的時間間隔(默認 1 毫秒)來跟蹤每一線程的堆棧信息(stack trace),並經過比較時間間隔之間的堆棧狀態,來推算出某個方法執行了多久,給出一個近似值。
在演示應用頭一項「Time Profiler: System Methods」中,我用插入排序(Insertion Sort)和冒泡排序(Bubble Sort)兩種算法來作性能比較,下面是 Swift 代碼:swift
/* 引用自:http://waynewbishop.com/swift/sorting-algorithms/ */ func insertionSort() { var x, y, key: Int for (x = 0; x < numberList.count; x++) { key = numberList[x] for (y = x; y > -1; y--) { if key < numberList[y] { numberList.removeAtIndex(y + 1) numberList.insert(key, atIndex: y) } } } } func bubbleSort() { var x, y, z, passes, key : Int for (x = 0; x < numberList.count; ++x) { passes = (numberList.count - 1) - x; for (y = 0; y < passes; y++) { key = numberList[y] if (key > numberList[y + 1]) { z = numberList[y + 1] numberList[y + 1] = key numberList[y] = z } } } }
這段代碼主要是對數組的添加和刪除,兩種方法執行起來耗時很少,但後臺發生的系統動做卻多得讓人眼暈。數組
能夠發現,代碼用到了不少間接依賴,這些都是支撐代碼運行的系統庫文件。由於處理大數據集比較消耗系統資源,因此要儘量地把繁重的操做放到後臺去作,上面的代碼就走的後臺線程。在上圖的 Call Tree 中能夠看到,被調用的堆棧名是 dispatch_worker_thread3。若是把它放到主線程去執行,程序確定會掛起。不信你註釋掉 dispatch_async 調用看一下。緩存
再來個圖片加載的例子。性能優化
這兒有三種圖片加載方法:服務器
咱們來看看 Time Profiler 算出的結果是否是跟預想的同樣。
進入演示應用第二項「Time Profiler: Our Methods」,點擊「Reload」十次來重複加載圖片,這樣能產生足夠的數據來分析。而後在 Time Profiler 圖表中經過拖拉鼠標選中要放大查看的區域,從 Call Tree 中雙擊調用了 .reload 方法那一行(上圖中加亮選中那一行),就會跳轉到對應的代碼行,所用時間也標註出來了。
看到誰最花時間了吧。雖然代碼沒什麼可優化的地方,但你們應該認識到緩存能發揮的做用。因此即便有時還得調用 loadSlowImage,多數狀況下把圖片緩存下來,仍是能省些資源佔用。
此外,我想再說說 Call Tree 的選項設置。
這些選項默認是不選的,但把它們勾選上能夠幫你更快定位到關鍵的代碼上,每每這也是問題的源頭。
須要添加其餘工具的話:
咱們常常須要從服務器下載大量圖片,特別是開發照片類的應用。但每每稍不注意,內存使用就會暴增,因此得保證把這些圖片緩存下來以便重複使用。下面來看看演示程序中內存分配的例子。
從圖中能夠看到,每次點擊「Reload」從新載入圖片時,內存都會出現使用峯值。應用先分配大量內存來替換原有圖片,而後再釋放掉這部份內存,可想而知這樣的操做效率高不了,並且若是要下載更大的文件,呃,局面大概會失控吧。
看一下堆棧列表第四行,ImageIO_PNG_Data 裏有 9 張處於活動狀態的圖片,佔用了12.38 MB 內存,這些都是沒被系統釋放或緩存的內存,因此致使堆內存分配升高。接下來再看看使用緩存後的效果。
使用了緩存庫(Swift Haneke)後,點「Reload」五次,這回在 Allocations 列表中卻看不到 ImageIO_PNG_Data 對象了,這說明它是空的,沒有任何圖像數據。同時,All Heap Allocations 的大小已從剛纔的 14.61 MB 降到了 2.51 MB。Anonymous VM(匿名虛擬內存)是系統爲程序預留的、可能會當即被重複使用的一部分可用內存。要防止程序崩潰,就別讓堆的尺寸增加太快。
還有就是,例子用的是異步方式來加載圖片,這樣用不着等到全部圖片下載完才能在界面中顯示。大多數圖像緩存庫都會把加載工做放到後臺,以免延長主線程的響應週期。
儘管 Apple 推出的 ARC 能夠有效防範內存泄漏,但出問題的機率仍是會有,Swift 也不例外。鑑於篇幅有限,本文就不涉及內存和 ARC 的工做原理了,具體能夠參考官方文檔。我會用代碼來觸發內存泄漏。
首先從最底層上說,當兩個對象相互創建了強引用(strong reference),當一個對象被釋放,另外一個對象因爲是強引用的關係不容許被釋放,此時 ARC 沒法肯定沒被釋放的對象到底還有沒有用,因而就致使了內存泄漏。
要解決這個問題,能夠將其中的一個對象中變量設爲 weak,不讓它出如今保留週期中。不少開發者在管理 view controller 時常會在內存泄漏上中招,覺得換了新的 controller,老的 controller 就被釋放回收了,其實還沒。這樣代碼一多,就會形成不少對象都沒被釋放。因此用這個工具把整個應用跑一遍,把那些斷鏈的強引用清理乾淨,會大有裨益。
除了上述這三類工具,Instruments 還有不少實用的工具,推薦你們根據本身的關注點,花些時間去學學。好比:
最後小總結下。我倒不想一味誇大 Instruments 的做用,若是應用跑得挺痛快,沒出現啥調皮行爲,大可把它忽略,等到問題來了再作優化。對於新手來講,花些時間瞭解 Instruments 的功能,多調試多積累經驗,這樣作出來的應用在用戶體驗上確定錯不了。
你最經常使用的 Instruments 工具都有哪些?歡迎與咱們分享。
原文:How To Use The 3 Instruments You Should Be Using 譯者:LeanCloud