iOS性能調優系列(全)


總結:

三類工具

 

  • 基礎工具 (NSLog的方式記錄運行時間.)
  • 性能工具.檢測各個部分的性能表現,找出性能瓶頸
  • 內存工具.檢查內存正確性和內存使用效率

 

性能工具:

可以衡量CPU的使用,時間的消耗,電池的消耗

一、Time Profile

啟動Time Profile:Xcode ——> Product ——> Profile ——> Time Profile

使用Time Profiler調試程序,能獲取到整個應用程序運行中所消耗的時間分布和百分比

使用Time Profile前有兩點需要注意的地方:

1、一定要使用真機調試

在開始進行應用程序性能分析的時候,一定要使用真機。因為模擬器運行在Mac上,然而Mac上的CPU往往比iOS設備要快。相反,Mac上的GPU和iOS設備的完全不一樣,模擬器不得已要在軟件層面(CPU)模擬設備的GPU,這意味着GPU相關的操作在模擬器上運行的更慢,尤其是使用CAEAGLLayer來寫一些OpenGL的代碼時候,這就導致模擬器性能數據和用戶真機使用性能數據相去甚遠

2、應用程序一定要使用發布配置

在發布環境打包的時候,編譯器會引入一系列提高性能的優化,例如去掉調試符號或者移除並重新組織代碼。另iOS引入一種"Watch Dog"[看門狗]機制,不同的場景下,“看門狗”會監測應用的性能,如果超出了該場景所規定的運行時間,“看門狗”就會強制終結這個應用的進程。開發者可以crashlog看到對應的日志,但Xcode在調試配置下會禁用"Watch Dog"

 


啟動后如圖

 


點擊左上角紅色按鈕,開始調試

 


調試時注意右下方選項

 

Separate by Thread:每個線程應該分開考慮,只有這樣你才能揪出那些大量占用CPU的"重"線程

Invert Call Tree:從上倒下跟蹤堆棧,這意味着你看到的表中的方法,將已從第0幀開始取樣,這通常你是想要的,只有這樣你才能看到CPU中話費時間最深的方法,也就是說FuncA{FunB{FunC}} 勾選此項后堆棧以C->B-A 把調用層級最深的C顯示在最外面

Hide System Libraries:勾選此項你會顯示你app的代碼,這是非常有用的。因為通常你只關心cpu花在你自己寫的代碼上的時間 而不是花在系統代碼上的時間

Flatten Recursion:遞歸函數, 每個堆棧跟蹤一個條目

Top Functions:一個函數花費的時間直接在該函數中的總和,以及在函數調用該函數所花費的時間的總時間。因此,如果函數A調用B,那么A的時間報告在A花費的時間加上B花費的時間,這非常有用,因為它可以讓你每次下到調用堆棧時挑最大的時間數字,歸零在你最耗時的方法

 


最終調試界面如圖

雙擊選中行查看代碼

發現循環中的UIImage *image = [UIImage imageNamed:imageName];語句的時間消耗最長,度娘之:

UIImage初始化方法有兩種

A:imagedNamed初始化

B:imageWithContentsOfFile初始化

二者不同之處在於,imageNamed默認加載圖片成功后會內存中緩存圖片,這個方法用一個指定的名字在系統緩存中查找並返回一個圖片對象,如果緩存中沒有找到相應的圖片對象,則從指定地方加載圖片然后緩存對象,並返回這個圖片對象

而imageWithContentsOfFile則僅只加載圖片,不緩存

大量使用imageNamed方式會在不需要緩存的地方額外增加開銷CPU的時間來做這件事,當應用程序需要加載一張比較大的圖片並且使用一次性,那么其實是沒有必要去緩存這個圖片的,用imageWithContentsOfFile是最為經濟的方式,這樣不會因為UIImage元素較多情況下,CPU會被逐個分散在不必要緩存上浪費過多時間

使用場景需要編程時,應該根據實際應用場景加以區分,UIimage雖小,但使用元素較多問題會有所凸顯

內存工具:

關心內存泄露和內存垃圾問題

一、Analyze靜態分析

一. 靜態內存分析

  1. 所謂靜態內存分析, 是指在程序沒運行的時候, 通過工具對代碼直接進行分析

    • 根據代碼的上下文的語法結構, 讓編譯器分析內存情況, 檢查是否有內存泄露
  2. 作用

    • 邏輯錯誤: 訪問未初始化的變量或者野指針
    • 聲明錯誤: 聲明了一個對象, 但是從未使用過
    • 內存管理錯誤: 內存泄露
    • 缺點: 靜態內存分析由於是編譯器根據代碼進行的判斷, 做出的判斷不一定會准確, 因此如果遇到提示, 應該去結合代碼上文檢查一下
  3. OC中的靜態內存分析:

    • 曾經在MRC環境下, OC的代碼需要手動管理內存, 任何對象的引用, 都要伴隨一次release操作, 否則很容易發生內存泄露, 因此在MRC環境下, 使用靜態內存分析很有必要
    • 而在目前的ARC環境下, 很少會發生內存泄露, 但是也會有很少數的情況導致內存泄露
      • 如Foundation對象與CoreFoundation對象的相互轉化, CoreFoundation對象享用ARC機制, 所以容易發生內存泄露
  4. Swift的靜態內存分析

    • Swift中, 使用了類型重映射機制, 他可以將對象轉換成能夠自動管理內存的對象, 不需要我們手動釋放, 因此Swift在內存管理方面更為安全

二. 內存分配

  1. 作用

    • 查看當前運行情況的內存分配
    • 查看使用過的內存有沒有釋放掉
  2. 關於App中加載圖片的注意點

    • -imageNamed:

      • 該方法用於加載小圖片/使用頻率高的圖片
      • 此方法加載過得圖片, 在App運行期間, 始終會保有緩存, 這個緩存是由系統管理的, 無法通過代碼銷毀緩存
      • 當系統察覺到內存消耗過高, 就會自動釋放這部分內存

          Search for an object whose name was set explicitly using the setName: method and currently resides in the image cache. 該方法首先會從圖片內存中查找 Search the app's main bundle for a file whose name matches the specified string. 如果沒有找到, 就在App的mainBundle中查找 Search the Application Kit framework for a shared image with the specified name. 如果bundle中也沒有, 就會去framework的庫中尋找
    • -imageWithContentsOfFile:

      • 該方法用於加載大圖片/使用率較低的圖片
      • 它只是加載一次圖片, 並且不會做緩存, 當對象釋放的時候, 內存也就被釋放掉了
      • 因此對於不常用的圖片應該使用這個方法
  3. 圖片在沙盒中的存在形式

    1. 如果當前項目部署版本 <= 6.x: 那么所有圖片就會直接暴露在沙盒的資源包中(main bundle), 不會進行壓縮為Assets.car文件
    2. 如果當前項目部署版本 >= 7.x
      • 放在Images.xcassets中的所有圖片, 都會被壓縮為Assets.car文件, 不會直接暴露在沙盒的資源包中
      • 而沒有放在Images.xcassets的圖片, 會直接暴露在沙盒的資源包中
    3. 使用對比
      • 壓縮為Assets.car的文件:
        • 這些圖片不會暴露在外, 但是無法或得到這些圖片的路徑, 只能通過圖片名-imageName來加載圖片, 並且會產生緩存
        • 小圖片/使用頻率高, 放在image.xcassets中
      • 未壓縮的圖片:
        • 圖片暴露在外, 可以通過imageWithContentOfFile來獲得圖片的路徑, 不會有緩存
        • 大圖片/使用頻率低, 如新特性界面, 放在外面

 

二、Leaks動態內存分析

對於有些內存泄漏情況通過靜態分析無法解決的,可以通過動態分析來發現,分析起來更有針對性

點擊XCode的Product菜單Profile啟動Instruments:

選擇Leaks,會自動啟動Leaks工具和IOS模擬器:

Leaks啟動后會開始錄制,隨着對模擬器運行的App的操作,可以在Leaks中查看內存占用的情況。

Leaks頂部分為兩欄:Allocations和Leaks

Allocations紀錄了內存分配,用來優化內存使用的

Leaks用來分析內存泄漏。ARC中引起的內存泄漏原因就是引用環。

看下效果圖


 

乍一看還不錯啊,tableview非常流暢,圖片能夠惰性下載(cell屏幕上再下載),這也是我想要的雛形。可是,好奇的用leaks分析一下


 

我擦

 然后,我們打開leaks,看看錯誤出現在哪

先選擇Leaks和Leaks by Backtrace.這里可以看到那些對象內存泄漏了,泄漏了多少,這個就是簡單看看,沒有太多調試意義。


 

然后看看Call Tree,因為Call Tree會給我們大概的位置,有時候會給我們精確的位置,不過要看運氣了。


 

然后,再又面選擇Invert Call Tree和Hide System Library


 

然后,我們就知道大概內存泄漏的位置在NSOperation的子類這里了。

然后雙擊上文圖片中的任意一行,就會跳到代碼處內存泄漏的地方(事實上,到這步,很多內存泄漏的問題都會被發現)

那么怎么辦呢?有問題下看文檔,我們看到圖片中引起引用計數加一的是

+ (NSURLSession*)sessionWithConfiguration:(NSURLSessionConfiguration*)configurationdelegate:(id)delegatedelegateQueue:(NSOperationQueue*)queue:

看下文檔,發現了這個地方


 

於是,我們要手動的去斷開強引用,於是,我們手動去斷開

-(void)setOperationFinished{ 

  [self.sessioninvalidateAndCancel]; 

  [selfwillChangeValueForKey:@"isFinished"];   

[selfwillChangeValueForKey:@"isExecuting"];   

executing =NO;    finished =YES;   

[selfdidChangeValueForKey:@"isExecuting"];   

[selfdidChangeValueForKey:@"isFinished"];

}

再運行下看看,能夠正常的Dealloc了

2015-06-0510:28:36.814AsyncImageTableviewDemo[1245:83664]dealloc2015-06-0510:28:36.954AsyncImageTableviewDemo[1245:83664]dealloc

用leaks分析,也沒有內存泄漏了


 

總結:其實大多數問題在雙擊上文的代碼部分就可以解決了,少數問題需要詳細的分析ARC引用過程

 

三、Zombie內存垃圾

程序掛了的問題比性能問題嚴重得多.
可以在Instrument中開Zombie功能,使得內存crash的時候,能看到crash在哪個對象上.
還是那幾個優缺點
這個東西一般在接到 EXEC_BAD_ACCESS的時候用.這個時候是內存訪問的問題.

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM