「原創譯文」iOS 性能優化:Instruments 工具的救命三招

你的 iOS 應用,運行速度靠譜嗎?中槍的同窗莫要愁,性能優化咱有妙招。用 Xcode 自家的調試工具 Instruments,揪出那些堵線程、佔內存、耗資源的問題代碼,完全破掉迷局,讓應用揚眉吐氣!html


對於每位 iOS 開發者來講,代碼性能是個避不開的話題。隨着項目的擴大和功能的增多,沒通過認真調試和優化的代碼,要麼任性地卡頓運行,要麼低調地崩潰了之……結果呢,你們用着不高興,開發者也不開心。ios

其實要破這個局面並不難,只要在 Xcode 自帶的監控調試工具 Instruments 上花點功夫,讓大代碼流暢運行也不是神話。Instruments 提供了不少功能,我會重點介紹一下我最經常使用的三大類:git

  • Time Profiler:分析代碼的執行時間,找出致使程序變慢的緣由。
  • Allocations:監測內存使用/分配狀況
    迅速膨脹的內存能夠很快讓程序斃命,因此要多加防範。
  • Leaks:找到引起內存泄漏的起點

即便有 ARC(自動引用計數)內存管理機制,但在現實中對象之間引用複雜,循環引用致使的內存泄漏仍然難以免,因此關鍵時刻還要自力更生。github

針對這三方面的測試,我寫了個演示應用,放在 GitHub 上,來幫助你們更直觀地瞭解這些工具的使用方法。好,進入正題。算法

001

Time Profiler

時間都去哪兒啦? 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
            }
        }
    }
}

這段代碼主要是對數組的添加和刪除,兩種方法執行起來耗時很少,但後臺發生的系統動做卻多得讓人眼暈。數組

004

能夠發現,代碼用到了不少間接依賴,這些都是支撐代碼運行的系統庫文件。由於處理大數據集比較消耗系統資源,因此要儘量地把繁重的操做放到後臺去作,上面的代碼就走的後臺線程。在上圖的 Call Tree 中能夠看到,被調用的堆棧名是 dispatch_worker_thread3。若是把它放到主線程去執行,程序確定會掛起。不信你註釋掉 dispatch_async 調用看一下。緩存

再來個圖片加載的例子。性能優化

003

這兒有三種圖片加載方法:服務器

  • loadSlowImage1:從指定 URL 下載一張圖片(加載速度慢)
  • loadImage2:從本地資源庫加載一張圖片(注意:沒用系統緩存)
  • loadFastImage3:從系統緩存中加載一張圖片(加載速度快)

咱們來看看 Time Profiler 算出的結果是否是跟預想的同樣。

進入演示應用第二項「Time Profiler: Our Methods」,點擊「Reload」十次來重複加載圖片,這樣能產生足夠的數據來分析。而後在 Time Profiler 圖表中經過拖拉鼠標選中要放大查看的區域,從 Call Tree 中雙擊調用了 .reload 方法那一行(上圖中加亮選中那一行),就會跳轉到對應的代碼行,所用時間也標註出來了。

004

看到誰最花時間了吧。雖然代碼沒什麼可優化的地方,但你們應該認識到緩存能發揮的做用。因此即便有時還得調用 loadSlowImage,多數狀況下把圖片緩存下來,仍是能省些資源佔用。

此外,我想再說說 Call Tree 的選項設置。

005

這些選項默認是不選的,但把它們勾選上能夠幫你更快定位到關鍵的代碼上,每每這也是問題的源頭。

  • Separate by Thread:按線程分開作分析,這樣更容易揪出那些吃資源的問題線程。特別是對於主線程,它要處理和渲染全部的接口數據,一旦受到阻塞,程序必然卡頓或中止響應。
  • Invert Call Tree:反向輸出調用樹。把調用層級最深的方法顯示在最上面,更容易找到最耗時的操做。
  • Hide Missing Symbols:隱藏缺失符號。若是 dSYM 文件或其餘系統架構缺失,列表中會出現不少奇怪的十六進制的數值,用此選項把這些干擾元素屏蔽掉,讓列表迴歸清爽。
  • Hide System Libraries:隱藏系統庫文件。過濾掉各類系統調用,只顯示本身的代碼調用。
  • Flattern Recursion:拼合遞歸。將同一遞歸函數產生的多條堆棧(由於遞歸函數會調用本身)合併爲一條。
  • Top Functions:找到最耗時的函數或方法。

須要添加其餘工具的話:

007

Allocations

咱們常常須要從服務器下載大量圖片,特別是開發照片類的應用。但每每稍不注意,內存使用就會暴增,因此得保證把這些圖片緩存下來以便重複使用。下面來看看演示程序中內存分配的例子。

008

從圖中能夠看到,每次點擊「Reload」從新載入圖片時,內存都會出現使用峯值。應用先分配大量內存來替換原有圖片,而後再釋放掉這部份內存,可想而知這樣的操做效率高不了,並且若是要下載更大的文件,呃,局面大概會失控吧。

看一下堆棧列表第四行,ImageIO_PNG_Data 裏有 9 張處於活動狀態的圖片,佔用了12.38 MB 內存,這些都是沒被系統釋放或緩存的內存,因此致使堆內存分配升高。接下來再看看使用緩存後的效果。

009

使用了緩存庫(Swift Haneke)後,點「Reload」五次,這回在 Allocations 列表中卻看不到 ImageIO_PNG_Data 對象了,這說明它是空的,沒有任何圖像數據。同時,All Heap Allocations 的大小已從剛纔的 14.61 MB 降到了 2.51 MB。Anonymous VM(匿名虛擬內存)是系統爲程序預留的、可能會當即被重複使用的一部分可用內存。要防止程序崩潰,就別讓堆的尺寸增加太快。

還有就是,例子用的是異步方式來加載圖片,這樣用不着等到全部圖片下載完才能在界面中顯示。大多數圖像緩存庫都會把加載工做放到後臺,以免延長主線程的響應週期。

Leaks

儘管 Apple 推出的 ARC 能夠有效防範內存泄漏,但出問題的機率仍是會有,Swift 也不例外。鑑於篇幅有限,本文就不涉及內存和 ARC 的工做原理了,具體能夠參考官方文檔。我會用代碼來觸發內存泄漏。

首先從最底層上說,當兩個對象相互創建了強引用(strong reference),當一個對象被釋放,另外一個對象因爲是強引用的關係不容許被釋放,此時 ARC 沒法肯定沒被釋放的對象到底還有沒有用,因而就致使了內存泄漏。

010

要解決這個問題,能夠將其中的一個對象中變量設爲 weak,不讓它出如今保留週期中。不少開發者在管理 view controller 時常會在內存泄漏上中招,覺得換了新的 controller,老的 controller 就被釋放回收了,其實還沒。這樣代碼一多,就會形成不少對象都沒被釋放。因此用這個工具把整個應用跑一遍,把那些斷鏈的強引用清理乾淨,會大有裨益。

除了上述這三類工具,Instruments 還有不少實用的工具,推薦你們根據本身的關注點,花些時間去學學。好比:

  • Core Data:監測讀取、緩存未命中、保存等操做,能直觀顯示是否保存次數遠超實際須要。
  • Cocoa Layout:觀察約束變化,找出佈局代碼的問題所在。
  • Network:跟蹤 TCP / IP和 UDP / IP 鏈接。
  • Automations:建立和編輯測試腳原本自動化 iOS 應用的用戶界面測試。

最後小總結下。我倒不想一味誇大 Instruments 的做用,若是應用跑得挺痛快,沒出現啥調皮行爲,大可把它忽略,等到問題來了再作優化。對於新手來講,花些時間瞭解 Instruments 的功能,多調試多積累經驗,這樣作出來的應用在用戶體驗上確定錯不了。

你最經常使用的 Instruments 工具都有哪些?歡迎與咱們分享。


原文:How To Use The 3 Instruments You Should Be Using 譯者:LeanCloud

相關文章
相關標籤/搜索