这周开始好好钻研一下LLDB相关的知识,这是一系列的文章。有些初级知识可能大家都有所涉猎,嘿嘿,懂得自然懂,看我的博客,什么时候会收获小。
基础语法
1.help
有啥不会,就直接输入help,你会得到如下的一系列信息,
command -- A set of commands for managing or customizing the
debugger commands.
disassemble -- Disassemble bytes in the current function, or elsewhere
in the executable program as specified by the user.
2.print
输出变量值
比如,对于如下的程序语句 let haha = 5;
你只要在LLDB里面输入 print haha
就可以得到如下输出结果。
(lldb) p haha
(Int) $R0 = 5
请注意,在与调试器共舞- LLDB 的华尔兹一文中曾经提出会输出类似于 $0 = 5
,并指出$0是当前的输出值。这个说话其实是不严谨的,正确的说法是,应该是当前的haha的值5存在了R0寄存器里。
比如,当我们构建如下的程序语句时,
let haha = 5;
let object = "jkjksdjf";
我们在分别print haha
以及 print object
就会分别得到(Int) $R0 = 5
和(String) $R1 = "jkjksdjf"
这说明haha的值和object的值分别以int型和string型存在了R0和R1寄存器之中。
当然,图快速的话,可以像我上面一样将print简写成p。
3.po
输出变量值
哎,有人奇怪了,po也是输出变量值,那和p有啥区别啊?本质上没啥区别,如果真要说,就是po = e -O –,具体我们后续再说啦。
4.breakpoubt l
输出所有的断点,可以得到如下的结果
Current breakpoints:
1: file = 'xxx/ViewController.swift', line = 19, locations = 1, resolved = 1, hit count = 1
1.1: where = xxx.ViewController.viewDidLoad (xxx.ViewController)() -> () + 131 at ViewController.swift:19, address = 0x0000000108d0a443, resolved, hit count = 1
其中 1: file的这个1就是ID号。
那这里的1.1是什么鬼?嘿嘿,当里使用Symbolic Breakpoint的时候,你一个断点很有可能截获了多个地方,比如AViewController和BViewController的viewDidLoad都被加上了断点,这个时候就需要靠诸如1.1和1.2之类的细分ID来进行区别了。
当然,有人会问,输出这个断点有什么用啊。嘿嘿,当你使用Xcode Symbolic Breakpoint的时候,你就会发现究竟在多少个地方下了断点了。
同样的,你可以将breakpoint简写成br。
5.br delete ID
这里的ID就是之前的断点的ID号
通过这个命令,可以删除ID对应的断点
6.br e ID
启用一个ID号对应的断点
7.br di ID
禁用一个ID号对应的断点
8.b xxx.swift:lineY
在xxx文件的第lineY行设置一个断点
如 b ViewController.swift:10
就是在ViewController的第10号下了一个断点。
需要注意的是,通过b命令设置的断点,无法直观的在Xcode界面上显示出来,而br delete
删除一个断点可以直接在Xcode上看出效果。
9.br set -n functionName
对functionName设置Symbolic Breakpoint
如br set -n viewDidLoad
就是对所有的viewDidLoad设置了Symboloc Breakpoint
10.br mod -C "Condition" ID
对ID号对应的breakpoint添加条件触发
假设我们有下面这样的一段代码
1. //ViewController.swift
2. for var value in money {
3. totalValue += value
4. }
我们首先先使用b ViewController.swift:3
设置一个断点,然后使用br l
查询到对应的ID为3。
然后我们使用br mod -C "totalValue > 50" 3
对这个断点设置条件触发,条件为当totalValue 大于50时候才触发。
当然,可能有些人会问,如果我不想删除断点,只是想移除条件触发怎么办?很简单,只要输入br mod -C "" ID
,将其中的Condition部分设置为空即可。
11.continue
继续运行程序
12.n
step over单步调试
13.s
step in进行函数
14.finish
step out退出函数
大杀器
上面的命令是不是很多,一个个敲实在是太麻烦,那如果我想对一个断点执行多条语句怎么办?
嘿嘿,大杀器来了。
br com add ID
对ID对应的断点进入交互式指定。如:
br com add 2
> bt
> continue
> DONE
上面的语句指的是,对2号断点进行交互式指定,当这个断点触发的时候,首先执行bt
(具体bt命令的意思我们后续再说,粗略理解就是backtrace输出调用栈,可以简单看下面的例子),然后执行continue
,最后通过关键字Done退出指定,这里的Done类似于shell里面的exit。
frame #0: xxx`xxx.ViewController.viewDidLoad (self=0x00007faf52439c20)() -> () + 470 at ViewController.swift:27
frame #1: xxx`@objc xxx.ViewController.viewDidLoad (xxx.ViewController)() -> () + 34 at ViewController.swift:0
frame #2: UIKit`-[UIViewController loadViewIfRequired] + 1198
frame #3: UIKit`-[UIViewController view] + 27
frame #4: UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 61
frame #5: UIKit`-[UIWindow _setHidden:forced:] + 282
frame #6: UIKit`-[UIWindow makeKeyAndVisible] + 42
frame #7: UIKit`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4131
frame #8: UIKit`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1760
frame #9: UIKit`-[UIApplication workspaceDidEndTransaction:] + 188
frame #10: FrontBoardServices`-[FBSSerialQueue _performNext] + 192
frame #11: FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 45
frame #12: CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
frame #13: CoreFoundation`__CFRunLoopDoSources0 + 556
frame #14: CoreFoundation`__CFRunLoopRun + 867
frame #15: CoreFoundation`CFRunLoopRunSpecific + 488
frame #16: UIKit`-[UIApplication _run] + 402
frame #17: UIKit`UIApplicationMain + 171
frame #18: xxx`main + 109 at AppDelegate.swift:12
frame #19: libdyld.dylib`start + 1
frame #20: libdyld.dylib`start + 1