lldb 是 Xcode 中集成的一個(gè)開(kāi)源調(diào)試器,可以減少我們每次調(diào)試程序時(shí)需要重新啟動(dòng)程序并逐級(jí)進(jìn)入到指定頁(yè)面的時(shí)間舌稀。
要使用 lldb 除了在代碼中加斷點(diǎn)外列肢,還可以在程序運(yùn)行時(shí)點(diǎn)擊控制臺(tái)上方第三個(gè)指令(Pause program execution)使程序進(jìn)入暫停狀態(tài)陕截。
查看 lldb 的具體命令,可以通過(guò)在控制臺(tái)輸入 help 后回車查看挣惰。
下面介紹一些常用的命令
Evaluate an expression on the current thread. Displays any returned value with LLDB's default formatting.
計(jì)算當(dāng)前線程上的表達(dá)式。使用LLDB的默認(rèn)格式顯示任何返回值殴边。
簡(jiǎn)寫(xiě):p
憎茂,使用如圖
可以看到
p
命令能打印出變量的值及其類型,其中expression
Evaluate an expression on the current thread. Displays any returned value with LLDB's default formatting.
計(jì)算當(dāng)前線程上的表達(dá)式募逞。使用LLDB的默認(rèn)格式顯示任何返回值。
如果要改變一個(gè)值馋评,可使用 expression 命令放接,簡(jiǎn)寫(xiě) p
,使用如下圖
e
不僅可以改變控制器中的值栗恩,還可以改變程序中的值透乾。注意
p
是 e
不帶參數(shù)的簡(jiǎn)寫(xiě)。當(dāng)使用
p
打印對(duì)象時(shí)磕秤,只會(huì)打印出指針地址乳乌,這時(shí)我們可以使用 po
(e
的另一種格式)來(lái)實(shí)現(xiàn)。p
命令還可以設(shè)置打印格式市咆,打印格式語(yǔ)法 pring/<fmt> 或 p/<fmt>默認(rèn)格式
// 十進(jìn)制
(lldb) p 16
16
// 十六進(jìn)制
(lldb) p/x 16
0x10
// 二進(jìn)制(t 代表 two)
(lldb) po/t 16
0b00000000000000000000000000010000
(lldb) po/t (char)16
0b00010000
lldb 定義變量
使用 lldb 定義變量需要在變量前以 $ 開(kāi)頭汉操,如下
(lldb) e int $a = 2
(lldb) p $a * 19
38
(lldb) e NSArray *$array = @[ @"Saturday", @"Sunday", @"Monday" ]
(lldb) p [$array count]
2
(lldb) po [[$array objectAtIndex:0] uppercaseString]
SATURDAY
(lldb) p [[$array objectAtIndex:$a] characterAtIndex:0]
error: no known method '-characterAtIndex:'; cast the message send to the method's return type
error: 1 errors parsing expression
注意最后一個(gè)報(bào)錯(cuò)了,因?yàn)闆](méi)有明確返回類型蒙兰,解決如下
(lldb) p (char)[[$array objectAtIndex:$a] characterAtIndex:0]
'M'
(lldb) p/d (char)[[$array objectAtIndex:$a] characterAtIndex:0]
77
流程控制相關(guān)
在調(diào)試程序的時(shí)候控制臺(tái)上方工具欄一部分如下圖
從左只有磷瘤,3-7分別為 continue \ step over \ step into \ step out
contine(c):取消程序的暫停,允許程序正常執(zhí)行下去直到遇到下一個(gè)斷點(diǎn)搜变。
step over(next 或 n):黑盒方式執(zhí)行下一行代碼采缚,如果所在行是一個(gè)方法調(diào)用,不會(huì)跳進(jìn)這個(gè)方法挠他,而是執(zhí)行這個(gè)方法扳抽,然后繼續(xù)。
step into(step 或 s):跳進(jìn)一個(gè)方法調(diào)試殖侵,若當(dāng)前行不是方法調(diào)用贸呢,作用和
n
相同。step out:如果不小心跳進(jìn)一個(gè)方法拢军,使用
step out
命令可以繼續(xù)執(zhí)行到下一個(gè)返回語(yǔ)句然后再次停止楞陷。
Thread Return
調(diào)試時(shí),使用 thread return 可以控制程序流程茉唉。
thread return
有一個(gè)可選參數(shù)固蛾,在執(zhí)行時(shí)它會(huì)把可選參數(shù)加載進(jìn)返回寄存器里,然后立刻執(zhí)行返回命令赌渣,跳出當(dāng)前棧幀魏铅。這意味著方法剩余的部分不會(huì)被執(zhí)行。(提前跳出方法)
這會(huì)給 ARC 的引用計(jì)數(shù)造成一些問(wèn)題坚芜,或者會(huì)使方法內(nèi)的清理部分失效览芳。但是在方法的開(kāi)頭執(zhí)行這個(gè)命令,是個(gè)非常好的隔離這個(gè)方法鸿竖,偽造返回值的方式 沧竟。
管理斷點(diǎn)
Xcode 左側(cè) Navigator 倒數(shù)第二個(gè)選項(xiàng)(看起來(lái)像一個(gè)斷點(diǎn))下铸敏,這是衣蛾可以快速管理所有斷點(diǎn)的面板。
lldb 同樣可以做這件事悟泵,breakpoint list
(br li
)命令可以查看當(dāng)前項(xiàng)目下所有的斷點(diǎn)及其位置杈笔。
breakpoint enable <breakpointID>
和breakpoint disable <breakpointID>
命令可以用來(lái)開(kāi)啟或關(guān)閉某個(gè)斷點(diǎn)。
如下圖
斷點(diǎn)
斷點(diǎn)相關(guān)的打算單獨(dú)寫(xiě)個(gè)帖子糕非,我使用 Xcode 的斷點(diǎn)蒙具,感覺(jué)比 lldb 命令方便點(diǎn)。
更新 UI
使用 recursiveDescription
方法可以遞歸返回指定 view 的層次結(jié)構(gòu)朽肥,如下圖
注意:
recursiveDescription
方法是系統(tǒng)私有方法禁筏,需要手敲,不會(huì)有代碼提示衡招。根據(jù)上面的方法篱昔,我們可以通過(guò)某個(gè) view 的地址獲取該 view,然后在調(diào)試器中改變它的顏色或其他一些屬性始腾。
// 獲取 view 的層次結(jié)果
po [self.view recursiveDescription]
<UIView: 0x7fcea74069a0; frame = (0 0; 428 926); autoresize = W+H; layer = <CALayer: 0x600001a38ec0>>
| <UILabel: 0x7fcea7407db0; frame = (100 100; 100 30); text = 'lldb調(diào)試器'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60000393c140>>
| <UIButton: 0x7fcea9008740; frame = (100 150; 150 30); opaque = NO; layer = <CALayer: 0x600001a0aba0>>
| | <UIButtonLabel: 0x7fcea7616950; frame = (10.6667 4; 129 22); text = '這是一個(gè)button'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x600003932a80>>
// 獲取 view 的指定子視圖 label
(lldb) e id $label1 = (id)0x7fcea7407db0
// 設(shè)置 label 的背景色
(lldb) e (void)[$label1 setBackgroundColor:[UIColor yellowColor]]
// 設(shè)置 label text 屬性
(lldb) e (void)[$label1 setText:@"在lldb調(diào)試器中更新 UI"]
// Continue program execution(點(diǎn)擊控制臺(tái)上方工具欄的過(guò)斷點(diǎn)按鈕)繼續(xù)運(yùn)行程序
(lldb) c
在 lldb 調(diào)試器中運(yùn)行上面的一系列命令后可以看到界面確實(shí)發(fā)生了變化州刽,如下圖
這種情況是只有程序繼續(xù)運(yùn)行才能看到界面的變化,因?yàn)楦淖兊膬?nèi)容必須發(fā)送到渲染服務(wù)中才會(huì)顯示更新浪箭。
渲染服務(wù)實(shí)際上是一個(gè)另外的進(jìn)程 (被稱作
backboardd
)穗椅。這就是說(shuō)即使我們正在調(diào)試的內(nèi)容所在的進(jìn)程被打斷了,backboardd
也還是繼續(xù)運(yùn)行著的奶栖。這意味著你可以運(yùn)行下面的命令房待,而不用繼續(xù)運(yùn)行程序:
(lldb) e (void)[CATransaction flush]
此時(shí)你仍然在調(diào)試器中,但是界面會(huì)實(shí)時(shí)更新驼抹。
頁(yè)面跳轉(zhuǎn)
我們可以使用 lldb 調(diào)試器在控制臺(tái)通過(guò)命令實(shí)現(xiàn) push 跳轉(zhuǎn)
// 創(chuàng)建跳轉(zhuǎn)目的地
(lldb) e id $vc = [[ViewController1 alloc] init]
(lldb) p self
(ViewController *) $1 = 0x00007fec5e808f60
// 執(zhí)行跳轉(zhuǎn)
(lldb) e (void)[self.navigationController pushViewController:$vc animated:YES]
// 將跳轉(zhuǎn)過(guò)程渲染到界面顯示
(lldb) e [CATransaction flush]
(lldb)
// 執(zhí)行 pop
(lldb) e (void)[self.navigationController popViewControllerAnimated:YES]
// 渲染--但是不好用,需要點(diǎn)擊繼續(xù)運(yùn)行結(jié)束調(diào)試或者輸入命令 `c` 結(jié)束調(diào)試才能執(zhí)行 pop拜鹤。
(lldb) e [CATransaction flush]
// 測(cè)試中發(fā)現(xiàn) present 跳轉(zhuǎn)也需要輸入命令 `c`(繼續(xù)運(yùn)行結(jié)束調(diào)試)才好用框冀。