總結:
三類工具
- 基礎工具 (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靜態分析
一. 靜態內存分析
-
所謂靜態內存分析, 是指在程序沒運行的時候, 通過工具對代碼直接進行分析
- 根據代碼的上下文的語法結構, 讓編譯器分析內存情況, 檢查是否有內存泄露
-
作用
- 邏輯錯誤: 訪問未初始化的變量或者野指針
- 聲明錯誤: 聲明了一個對象, 但是從未使用過
- 內存管理錯誤: 內存泄露
- 缺點: 靜態內存分析由於是編譯器根據代碼進行的判斷, 做出的判斷不一定會准確, 因此如果遇到提示, 應該去結合代碼上文檢查一下
-
OC中的靜態內存分析:
- 曾經在MRC環境下, OC的代碼需要手動管理內存, 任何對象的引用, 都要伴隨一次release操作, 否則很容易發生內存泄露, 因此在MRC環境下, 使用靜態內存分析很有必要
- 而在目前的ARC環境下, 很少會發生內存泄露, 但是也會有很少數的情況導致內存泄露
- 如Foundation對象與CoreFoundation對象的相互轉化, CoreFoundation對象享用ARC機制, 所以容易發生內存泄露
-
Swift的靜態內存分析
- Swift中, 使用了類型重映射機制, 他可以將對象轉換成能夠自動管理內存的對象, 不需要我們手動釋放, 因此Swift在內存管理方面更為安全
二. 內存分配
-
作用
- 查看當前運行情況的內存分配
- 查看使用過的內存有沒有釋放掉
-
關於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:
- 該方法用於加載大圖片/使用率較低的圖片
- 它只是加載一次圖片, 並且不會做緩存, 當對象釋放的時候, 內存也就被釋放掉了
- 因此對於不常用的圖片應該使用這個方法
-
-
圖片在沙盒中的存在形式
- 如果當前項目部署版本 <= 6.x: 那么所有圖片就會直接暴露在沙盒的資源包中(main bundle), 不會進行壓縮為Assets.car文件
- 如果當前項目部署版本 >= 7.x
- 放在Images.xcassets中的所有圖片, 都會被壓縮為Assets.car文件, 不會直接暴露在沙盒的資源包中
- 而沒有放在Images.xcassets的圖片, 會直接暴露在沙盒的資源包中
- 使用對比
- 壓縮為Assets.car的文件:
- 這些圖片不會暴露在外, 但是無法或得到這些圖片的路徑, 只能通過圖片名-imageName來加載圖片, 並且會產生緩存
- 小圖片/使用頻率高, 放在image.xcassets中
- 未壓縮的圖片:
- 圖片暴露在外, 可以通過imageWithContentOfFile來獲得圖片的路徑, 不會有緩存
- 大圖片/使用頻率低, 如新特性界面, 放在外面
- 壓縮為Assets.car的文件:
二、Leaks動態內存分析
對於有些內存泄漏情況通過靜態分析無法解決的,可以通過動態分析來發現,分析起來更有針對性
點擊XCode的Product菜單Profile啟動Instruments:
選擇Leaks,會自動啟動Leaks工具和IOS模擬器:

Leaks啟動后會開始錄制,隨着對模擬器運行的App的操作,可以在Leaks中查看內存占用的情況。
Leaks頂部分為兩欄:Allocations和Leaks

Allocations紀錄了內存分配,用來優化內存使用的
Leaks用來分析內存泄漏。ARC中引起的內存泄漏原因就是引用環。
看下效果圖

乍一看還不錯啊,tableview非常流暢,圖片能夠惰性下載(cell屏幕上再下載),這也是我想要的雛形。可是,好奇的用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的時候用.這個時候是內存訪問的問題.