Xcode 調試技巧 --經常使用命令和斷點

Xcode 中的調試技巧與咱們的平常開發息息相關,而這些調試技巧在咱們解決Bug時,經常有事半功倍的做用,常常會用到的有各類斷點 和 命令。而這些調試技巧也常常會在面試中問到,因此不知道的就來看看吧。面試

調試主要觀看區

調試命令

在上圖中,右側綠色區域就是Log 輸出區,在 Log 輸出區可使用一些命令,來輔助調試。express

那有哪些調試命令呢?bash

想要看全部的調試命令,能夠在上圖的右側區域輸入help,就會列出全部的調試命令。 本文就介紹幾個使用頻率比較高的,其餘就查看後,自行了解吧。app

1. p 命令

-- ('expression --')  Evaluate an expression on the current thread.
                      Displays any returned value with LLDB's default formatting. 複製代碼

p 命令是 print 命令的簡寫,使用p 命令能夠查看基本數據類型的值,可是若是 使用 p 命令 查看的是對象,那麼只會返回對象的指針地址。函數

p 命令後面除了能夠接 變量、常量,還能夠接 表達式。(❌可是不可使用宏❌)oop

2. po 命令

po 命令能夠理解爲打印對象。功能與 p 命令相似,因此也是能夠打印 常量、變量,打印表達式返回的對象等。(❌也不能夠打印宏❌)lua

p 和 po 使用範例

固然,這些打印功能,除了使用命令外,咱們也可使用左側區域,點擊變量右鍵---> print Description of 「xxx」: spa

Paste_Image.png

固然還有其餘的打印方法:線程

3.expr 命令

expr 是 expression 的簡寫, 使用expr 命令,可以在調試時,動態的執行賦值表達式,同時打印出結果。咱們能夠在調試時,動態的修改變量的值,這在調試想要讓應用執行異常路徑(如執行某個else 狀況)頗有用。3d

(lldb) p i 
(NSInteger) $16 = 1
(lldb) expression i = 5
(NSInteger) $17 = 5
(lldb) po i 
5
複製代碼

4.call 命令

上面是動態修改變量的值, Xcode 還支持動態調用函數。在控制檯執行該命令,能夠在不修改代碼,不從新編譯的狀況下,修改界面上的視圖。 這裏有一個動態將cell 的某個子視圖移除的範例:

(lldb) po cell.contentView.subviews
<__NSArrayM 0x60800005f5f0>(
<UILabel: 0x7f91f4f18c90; frame = (5 5; 300 25); text = '2 - Drawing index is top ...'; userInteractionEnabled = NO; tag = 1; layer = <_UILabelLayer: 0x60800009ff40>>,
<UIImageView: 0x7f91f4d20050; frame = (105 20; 85 85); opaque = NO; userInteractionEnabled = NO; tag = 2; layer = <CALayer: 0x60000003ff60>>,
<UIImageView: 0x7f91f4f18f10; frame = (200 20; 85 85); opaque = NO; userInteractionEnabled = NO; tag = 3; layer = <CALayer: 0x608000039860>>
)

(lldb) call [label removeFromSuperview]
(lldb) po cell.contentView.subviews
<__NSArrayM 0x600000246de0>(
<UIImageView: 0x7f91f4d20050; frame = (105 20; 85 85); opaque = NO; userInteractionEnabled = NO; tag = 2; layer = <CALayer: 0x60000003ff60>>,
<UIImageView: 0x7f91f4f18f10; frame = (200 20; 85 85); opaque = NO; userInteractionEnabled = NO; tag = 3; layer = <CALayer: 0x608000039860>>
)
複製代碼

5.bt命令

bt 命令 能夠打印出線程的堆棧信息,該信息比左側的Debug Navigator 看到的還要詳細一些。

bt 命令是打印當前線程的堆棧信息

(lldb) bt 
* thread #1: tid = 0x27363, 0x000000010d204125 TestDemo`-[FifthViewController tableView:cellForRowAtIndexPath:](self=0x00007f91f4e153c0, _cmd="tableView:cellForRowAtIndexPath:", tableView=0x00007f91f5889600, indexPath=0xc000000000400016) + 2757 at FifthViewController.m:91, queue = 'com.apple.main-thread', stop reason = breakpoint 6.1
  * frame #0: 0x000000010d204125 TestDemo`-[FifthViewController tableView:cellForRowAtIndexPath:](self=0x00007f91f4e153c0, _cmd="tableView:cellForRowAtIndexPath:", tableView=0x00007f91f5889600, indexPath=0xc000000000400016) + 2757 at FifthViewController.m:91
    frame #1: 0x0000000111d0a7b5 UIKit`-[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 757
    frame #2: 0x0000000111d0aa13 UIKit`-[UITableView _createPreparedCellForGlobalRow:willDisplay:] + 74
    frame #3: 0x0000000111cde47d UIKit`-[UITableView _updateVisibleCellsNow:isRecursive:] + 3295
    frame #4: 0x0000000111d13d95 UIKit`-[UITableView _performWithCachedTraitCollection:] + 110
    frame #5: 0x0000000111cfa5ef UIKit`-[UITableView layoutSubviews] + 222
    frame #6: 0x0000000111c61f50 UIKit`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1237
    frame #7: 0x00000001117a5cc4 QuartzCore`-[CALayer layoutSublayers] + 146
    frame #8: 0x0000000111799788 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 366
    frame #9: 0x0000000111799606 QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 24
    frame #10: 0x0000000111727680 QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 280
    frame #11: 0x0000000111754767 QuartzCore`CA::Transaction::commit() + 475
    frame #12: 0x00000001117550d7 QuartzCore`CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 113
    frame #13: 0x0000000110743e17 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 23
    frame #14: 0x0000000110743d87 CoreFoundation`__CFRunLoopDoObservers + 391
    frame #15: 0x0000000110728b9e CoreFoundation`__CFRunLoopRun + 1198
    frame #16: 0x0000000110728494 CoreFoundation`CFRunLoopRunSpecific + 420
    frame #17: 0x0000000114390a6f GraphicsServices`GSEventRunModal + 161
    frame #18: 0x0000000111b9d964 UIKit`UIApplicationMain + 159
    frame #19: 0x000000010d21294f TestDemo`main(argc=1, argv=0x00007fff529fe620) + 111 at main.m:14
    frame #20: 0x000000011458a68d libdyld.dylib`start + 1
(lldb) 
複製代碼

bt all 命令是打印全部線程的堆棧信息。打印出來的信息太多,就不展現了!

6.image 命令

image list 命令能夠列出當前App中的全部module(這個module 在後面符號斷點時有用到),能夠查看某一個地址對應的代碼位置。 除了 image list 還有 image addimage lookup等命令,能夠自行查看。 當遇到crash 時,查看線程棧,只能看到棧幀的地址,使用 image lookup –address 地址 能夠方便的定位到這個地址對應的代碼行。

斷點

Xcode 中的斷點也是頗有學問的,有普通斷點、條件斷點、符號斷點、異常斷點等不少種。

1.普通斷點

打一個普通斷點,只須要找到對應的行,在代碼左側(行號上)點擊一下便可。

2.條件斷點

條件斷點是一種頗有用的斷點,特別是在for 循環中。若是咱們須要在i = 5 時添加斷點,其餘時候不加,那麼就可使用條件斷點。條件斷點是在普通斷點上 右鍵,選擇 Edit Breakpoint...,再設置一個條件便可

編輯普通斷點

添加條件

3.符號斷點

符號斷點就是 Symbolic Breakpoint,實際上是針對某一個特定函數的斷點,能夠是一個 OC函數,也能夠是 C++函數。 添加的地方以下:

符號斷點

符號斷點條件
Symbol 欄 能夠填 [類名 方法名]或者 方法名 ,module 也是選填項,它就是上面  image  命令中列出來的module。 例如 ,咱們若是隻填一個viewDidLoad,那麼就會在全部類(包括第三方庫)的viewDidLoad 處打斷點。

符號斷點在調試一些沒有源碼的模塊時比較有用,好比調試一個第三方提供的Lib庫,或者系統的模塊,能夠在相應函數處下斷點,能夠大概調試清楚程序的運行流程,也能夠在斷點的時候查看到參數信息。

4.異常斷點

若是程序運行就崩潰,咱們能夠打一個異常斷點,這樣崩潰時就會觸發斷點,很容易定位到問題所在,也能看到更多的崩潰相關信息,如Log,函數調用棧。

異常斷點

能夠修改異常斷點的條件

注意: 有的程序或者有的功能可能會使用異常來組織程序邏輯,好比調用AVAudioPlayer ,運行到 AVAudioPlayer 時,就會致使斷點被觸發。咱們能夠修改 Exception 參數,或者取消掉異常斷點來解決。

5.Watch 斷點

當某個變量發生變化的時候會觸發。 建立一個Watch斷點:

Watch 斷點

關於 Xcode 調試技巧中的 斷點和命令就先這麼多了,其餘有用到的之後再補充。

相關文章
相關標籤/搜索