iOS 調試技能 - bug定位、性能調試、常見問題分析

1. 常用調試方式

Print VS 單步調試

說到調試,剛入門編程時,用得最多的無疑是 print,畢竟連教材都是這樣寫的,直接打印,簡單明了。但是當打印內容太多時,就容易看得頭暈腦脹了,這里以 Swift 為例,稍微改進下 print 方法:

/**
print log
#file       String    包含這個符號的文件的路徑
#line       Int       符號出現處的行號
#column     Int       符號出現處的列
#function   String    包含這個符號的方法名字
*/
func printLog<T>(_ message: T,
                    file: String = #file,
                    method: String = #function,
                    line: Int = #line)
{
    #if DEBUG
    print("\((file as NSString).lastPathComponent)[\(line)], \(method): \(message)")
    #endif
}

打印的時候輸入文件的路徑,行列號和方法明等,這樣更方便通過 print 的日志來精準定位問題。但是通過 print 來調試時,有時候就不容易發現一些邏輯上的問題,或者需要使用大量的 print 輸出日志,這個時候就可以考慮使用單步調試:

ADD2B91E-EA8A-4C0B-976A-9E9BE78C27E9.png

單步調試的時候可以逐步查看代碼的執行過程,是解決 bug 的神器,如果你是一位新手,這肯定是你首要學會的技能,在單步調試的時候,一般都會結合 LLDB 命令來使用,關于 LLDB 的內容下面會詳細說。

但在實際開發中,有些場景是無法通過單步調試來復現的,比如說多線程的場景下,在使用單步時,很多時候是無法復現真實場景的,這個時候就需要使用萬能的 print 了。總之,兩種方式是必不可少的,在實際開發中,很多時候我們不會直接使用 print,一般都會使用日志框架,來對日志進行和記錄和收集。

Crash Report

在我們平常的開發中,你提交測試包給 QA 測試時,他那邊出現在了 crash,但卻不是調試模式下,這里我們就可以通過通過測試設備,在 Xcode 的 Devices 中把 crash 日志導出來:

5786A795-FC55-4A6F-8C3D-8CFE014257CC.png

關于如何去閱讀 Crash report 和定位該 Crash 原因,可以看我之前寫的文章:淺談 Crash Report,這里就不再重復談。

dSYM 文件

首先來科普下什么是 dSYM 文件:

Xcode編譯項目后,我們會看到一個同名的 dSYM 文件,dSYM 是保存函數地址映射信息的文件,調試的 symbols 都會包含在這個文件中,并且每次編譯項目的時候都會生成一個新的 dSYM 文件。

應用上架后,可以通過類似友盟統計等工具收集線上的 Crash,這里直接以友盟的為例,先看下 Crash 信息:

601D48A0-954A-4A0B-9AE3-252E7C966999.png

這里可以看出這個錯誤的原因是數組越界了,那么問題來了,我們并不知道是哪里越界,上面只給出了一個內容地址:

5   YHRSS                        0x1000420b0 YHRSS + 270512
6   YHRSS                        0x100041378 YHRSS + 267128

這時就可以通過 dSYM 文件來定位出問題的地方了。首先通過 archives 來找到 dSYM 文件:

步驟1:


步驟1.png

步驟2:


步驟2.png

步驟3:


步驟3.png

我們 cd 到該文件目錄下,然后執行:

atos -arch arm64 -o YHRSS 0x1000420b0

注意這里的 -arch 是和上面 crash 報告中的對應,否則是看不到相應的信息的:

$ atos -arch arm64 -o YHRSS 0x1000420b0
specialized YHArticlesViewController.tableView(_:heightForRowAt:) (in YHRSS) (YHArticlesViewController.swift:215)

這樣我們能就精準地獲取 crash 出現的具體位置,然后就該是發揮自我價值的時候了。

那些項目中遇到的常見問題定位

循環引用快速定位和解決

如果你懷疑存在循環引用,你可以 Instrument 工具來定位,但這也太麻煩了,你可以直接在 deinit{} 方法( OC 中對應的就是 dealloc 方法)里面打一個斷點,如果頁面退出時沒有執行到該處,就說明該頁面存在循環引用,頁面內存沒有辦法釋放。

如果存在循環引用,那么首先要檢查的是 block 里面的 self 是不是需要 weak,自定義的 delegate 是不是寫成了 strong,絕大部分都是這兩個原因導致的,逐個去檢查就好。

2. LLDB 常用命令的使用

什么是 LLDB

LLDB 是 Xcode 內置的調試工具,它與 LLVM 編譯器一起,給開發者提供更豐富的流程控制和數據檢測的調試功能,它的主要功能是為 Xcode 提供底層調試環境。

常用命令

  1. help 最牛逼的命令
    help 可以輸出 LLDB 的命令,使用 help <command> 可以輸出相應命令的 help。

    圖片.png
  1. po、p 打印值

    圖片.png

    po 和 p 的區別在于使用po只會輸出對應的值,p 則會返回值的類型以及命令結果的引用名。

  2. exp 輸出或修改值(主要作用是修改值)

    圖片.png
  3. bt 當前線程的調用堆棧,可能通過后面添加數字來限制輸出線程數,如 bt 5,只輸出前5個。

    52245C67-3376-4A95-A323-A3875BEBF2F9.png
  4. thread return 跳出當前方法的執行(thread return 0 設置返回值),但在 swift 中,是無法使用的,已知的問題了,只能等待修復吧,這里給個 OC 的例子:

    51084CDC-031A-490C-A0BE-474DA5C6B423.png

3. Instrument 的使用

寫在最前面,在做性能測試的時候,不能用模擬器,用真機,用真機,用真機,重要的事情說三次。

Time Profiler

time profile 是時間分析工具,主要用來檢測應用 CPU 的使用情況,可以看到應用程序中各個方法消耗 CPU 時間。關于概念,這里就不詳細介紹了,直接進行實際操作:

  1. 通過 xcode 中的 product --> profile 來啟動 Instrument,并選擇 Time Profiler 工具:

    BAE9B1B4-19D3-447D-9D59-42955BC22114.png
  2. 運行 Time Profiler,配置顯示方式,分線程顯示和隱藏系統的無關內容:

    B6CA42E2-0AEC-4787-AA39-C1B48FD7CF1F.png
  3. 在手機上執行想要測試的操作,執行完后停止 Time Profiler 進行分析:

    CBE4F6B3-6DD8-4CC0-8280-8F15A599B425.png
  4. 找到主要耗時的地方,并定位到具體的代碼行(點擊方法的小箭頭就可以進入相應的代碼處):

    11C6F965-91F3-4A44-ACF8-1F2FC03F2DC3.png

    這里可以看出,主要有兩一個耗時的操作,但明顯后面那我格式轉換我們沒有辦法去處理,我們只能從第一個入手。它每次創建都比較耗時,那么我們就不要多次去創建,因為它每次使用的格式的都是一樣的,這樣我們實質上只需要創建一次就可以了。那我們有什么方法去只創建一次呢,首先能想到的肯定是單例,但是用單例太麻煩了,通過 static 定義成一個常量就可以了,就這樣,這處的性能問題就解決了,其它地方也可以通過同樣的方法,逐步分析和解決就可以了。

Leaks

使用的步驟幾乎和上面的一樣,這里就不重復上圖了,但在出現內存泄露的地方,我們需要手動去選取對應的位置,這樣才方便分析問題:

3404A8AC-066D-4800-89BE-9DB013D2B2B0.png

因為 Leaks 的使用和 Time Profiler 是一樣的,這里就不去重復描述使用過程,在這里簡單地介紹下會出現內存泄露的常見情況:

  1. 循環引用
  2. ARC 中使用 C 方式開辟的內存沒有手動釋放
  3. URLSession 對象的多次創建,使用 AFNetworking 時也是同理

這里單獨針對 URLSession 對象的多次創建會導致內存泄露問題單獨說明下,其實我們只要看過 URLSession 的文檔我們就知道了:

Important

The session object keeps a strong reference to the delegate until your app exits or explicitly invalidates the session. If you don’t invalidate the session, your app leaks memory until it exits.

session 對象會一直持有強引用,導致無法釋放,多次創建就會有內存泄露問題,關于 session 的使用,我們應該使用一個單例來管理。而且唯一的 session 還可以加快網絡請求,如連接復用等,這里就不詳細說,回頭有時間再單獨寫一篇相關的文章,畢竟這不是一兩句話就能說清楚的事情。

參考文獻

  1. 與調試器共舞 - LLDB 的華爾茲
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容