IOS Xcode調試常用命令和斷點
Xcode 中的調試技巧與我們的日常開發息息相關,而這些調試技巧在我們解決Bug時,常常有事半功倍的作用,經常會用到的有各種斷點 和 命令。而這些調試技巧也經常會在面試中問到,所以不知道的就來看看吧。
調試命令
在上圖中,右側綠色區域就是Log 輸出區,在 Log 輸出區可以使用一些命令,來輔助調試。
那有哪些調試命令呢?
想要看所有的調試命令,可以在上圖的右側區域輸入help
,就會列出所有的調試命令。
本文就介紹幾個使用頻率比較高的,其他就查看後,自行了解吧。
1. p 命令
-- ('expression --') Evaluate an expression on the current thread. Displays any returned value with LLDB's default formatting.
p 命令是 print 命令的簡寫,使用p 命令可以查看基本數據類型的值,但是如果 使用 p 命令 查看的是對象,那麼只會返回對象的指針地址。
p 命令後面除了可以接 變量、常量,還可以接 表達式。(❌但是不可以使用宏❌)
2. po 命令
po 命令可以理解為打印對象。功能與 p 命令類似,所以也是可以打印 常量、變量,打印表達式返回的對象等。(❌也不可以打印宏❌)
當然,這些打印功能,除了使用命令外,我們也可以使用左側區域,點擊變量右鍵—> print Description of “xxx”:
當然還有其他的打印方法:
3.expr 命令
expr 是 expression 的簡寫, 使用expr 命令,能夠在調試時,動態的執行賦值表達式,同時打印出結果。我們可以在調試時,動態的修改變量的值,這在調試想要讓應用執行異常路徑(如執行某個else 情況)很有用。
(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 add
、image 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斷點:
關於 Xcode 調試技巧中的 斷點和命令就先這麼多了,其他有用到的以後再補充。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!