前言

曾经有个笑话(身边的真相)。有次,我要回去写代码的时候呢,我无意间说了一句‘我们回去写BUG吧’。然后传为组内佳话。
BUG对于我们程序员来说是无所不在的,你想你的代码没有BUG,除非你没写代码。对于我们iOS or macOS 开发的同学们来说,苹果为我们准备了一个非常良好的Debug的生态环境(LLDB)这使得我们能够非常快速,有效的去排除,解决一些问题。

那么本文主要以本人平时的开发习惯/WWDC 2018 Session 412/Advaced Apple Debug书中部分内容进行如下总结了一些LLDB的命令

LLDB

作为iOS开发者,我们经常回去设置一些断点,当程序运行到这一行的时候,我们就可以进入LLDB的控制台,去打印一些信息,来辅佐我们进行调试工作。但是我们平常只会去使用p/e命令吧?(至少我以前是这样的。)但是我们今天准备来系统的学习一下LLDB那些比较有作用的命令。在LLDB控制面板中打入help,我们可以看到一堆的命令。今天我准备拿出其中几个用的比较多的来进行说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Demo代码,下面的说明都会围绕这个代码进行描述
@interface HZViewController ()
@property (nonatomic, strong) NSString *string;
@property (nonatomic, strong) HZModel *model;
@end
@implementation HZViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
}
@end

expression

expression作为我们最常用的指令之一,他的作用执行一个表达式,并将表达式的结果作为结果进行输出,expression的完整语法

1
expression <cmd-options> -- <expr>

  • : 命令参数选项,可以根据help expression进行查看他有哪些命令集合。
  • : 表达式部分,也就是代码部分

    expression的简写为e,当你使用默认参数的时候可以不用打–这个多余的符号,但是当你使用参数的时候就必须加上。不过一般来说我们无需手动去进行设置。
    OK,我们来实践一下。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 最初String并没有被初始化(e也可以被当做是一个打印命令来打印信息。)
    (lldb) e self.string
    (NSString *) $6 = nil
    // 这个时候我们去执行一段表达式,去修改string的变量
    (lldb) e self.string = @"TestString"
    (NSTaggedPointerString *) $7 = 0xbf4c3b53c7710e1d
    // 我们去打印这个值
    (lldb) po self.string
    TestString

print

print相对来说很简单,其实他就是expression – 就是一个简化版的e,其简写为p。

1
print <expr>

1
2
3
4
(lldb) p self.string = @"asd"
(NSTaggedPointerString *) $12 = 0xbf623d41451e6fa4 @"\372\346Q\324#\366"
(lldb) po self.string
asd

po

如果expression不加任何参数,那么他打印出来的信息都是指针的信息。这对于我们调试来说是非常不方便的。
所以expression有一个-O的参数,是用来打印函数对象的,并且他的描述即为,打印具有语言的描述性API。
po其实就是expression -O 的简写,Xcode团队很人性化的加入了这个东西。当然上面的描述性API就是指的descriptiondebugDescription了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//为上面的代码加入以下代码 HZViewController.m中
- (NSString *)debugDescription {
return [NSString stringWithFormat:@" 类名:%@ \n 属性列表:%@",NSStringFromClass(HZViewController.class),HZViewController.hz_propertyKeys];
}
// LLDB调试输出
(lldb) po self // 尝试使用po命令还输出self
类名:HZViewController
属性列表:(
string,
model
)
(lldb) p self
(HZViewController *) $1 = 0x00007f934e50f4d0
(lldb) e -O -- self // 对比于po是没有任何区别的
类名:HZViewController
属性列表:(
string,
model
)

call

其实还有一个打印命令是call,对比于其他打印命令来说,他有一个优势在于,他能执行多行代码(但是我觉得比较鸡肋,我从来都没用过)。
如果单纯使用call 的话他跟e/p无差异。但是你单独使用call,就可以键入多行函数,来执行一些定制化的需求。比如下面这个。

1
2
3
4
5
6
7
8
9
10
11
12
13
(lldb) po self.view.backgroundColor
nil
(lldb) call // 进入函数输入控制
Enter expressions, then terminate with an empty line to evaluate:
1 if (self.view.backgroundColor == nil) {
2 self.view.backgroundColor = [UIColor whiteColor];
3 }
(lldb) po self.view.backgroundColor
UIExtendedGrayColorSpace 1 1
(lldb) call self.view.backgroundColor
(UICachedDeviceWhiteColor *) $10 = 0x0000600001c46070
(lldb) e self.view.backgroundColor
(UICachedDeviceWhiteColor *) $11 = 0x0000600001c46070

p/e/po/call区别

命令 主要区别 函数提示
e 打印属性(基础类型:包含函数类型和变量值,对象类型:包含函数指针) 没有命令行提醒,完全属于盲打
p 同e 终端会根据当前堆栈,提示属性/函数信息
po 打印具有平台特性的描述性API(基础类型:打印变量值,对象: 根据描述API输出) 终端会根据当前堆栈,提示属性/函数信息
call 打印功能同e,但是还具备输入多行表达式的功能 没有命令行提醒,完全属于盲打

断点

断点设置命令,可能会有人问了,我用Xcode,在函数边上点一下不就有断点了么,要这个命令有个锤子用?那只能说你作死作的还不够多。当你没有源码的时候,这个命令就成了你的救命稻草。举个🌰,我有个砸过壳的IPA包,纯二进制,你只能通过class-dump或者IDA去获取到一些函数信息,然后你就可以悄然的去用这个命令去设置一些断点,调试当前堆栈里的信息。整个APP都在你的掌控之中。OK,废话B多了,脑子要坏掉的,我们继续来看下这个命令。

breakpoint的缩写为br,后面都会用缩写来进行描写

br set

breakpoint set命令是用来设置断点的,LLDB提供了很多设置断点的方式。

1
breakpoint set <cmd-options>

使用 -n 来根据名称来设置一个断点
给所有的init方法设置上断点。

1
2
3
4
5
6
7
8
9
(lldb) br set -n init
Breakpoint 3: 3657 locations. // 可以看到设置了3657的断点 2333333
(lldb) br list
Current breakpoints:
3: name = 'init', locations = 3657, resolved = 3657, hit count = 0
3.1: where = CoreGraphics`ttf::cvt::init(), address = 0x0000000101042ec6, resolved, hit count = 0
3.2: where = CoreGraphics`ttf::font::init(), address = 0x000000010148c9ca, resolved, hit count = 0
3.3: where = CoreGraphics`ttf::fpgm::init(), address = 0x0000000101063086, resolved, hit count = 0
3.4: where = CoreGraphics`ttf::glyf::init(), address = 0x00000001012bacea, resolved, hit count = 0

使用 -f 来根据文件名去配合设置断点
我只想给HZViewController的init设置断点,其他断点都不要,因为前面设置的断点太多了。

1
2
(lldb) br set -f HZViewController.m -n init
Breakpoint 3: where = HZTest_Example`-[HZViewController init] + 20 at HZViewController.m:35, address = 0x000000010b6eeba4

Note: 文件的使用条件是,这个文件中必须包含有init函数。集成的话是不会被标记出来的。会提示WARNING: Unable to resolve breakpoint to any actual locations.

使用 -l 来根据文件来指定断点行数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
26:@property (nonatomic, strong) NSString *string;
34:- (instancetype)init {
35: if (self = [super init]) {
36:
37: }
38: return self;
39:}
(lldb) br set -l 26 -f HZViewController.m
Breakpoint 6: 2 locations.
(lldb) br list
Current breakpoints:
6: file = 'HZViewController.m', line = 26, exact_match = 0, locations = 2, resolved = 2, hit count = 0
6.1: where = HZTest_Example`-[HZViewController string] + 12 at HZViewController.m:26, address = 0x000000010f3c2f0c, resolved, hit count = 0
6.2: where = HZTest_Example`-[HZViewController setString:] + 49 at HZViewController.m:26, address = 0x000000010f3c2f51, resolved, hit count = 0
(lldb) br set -l 35 -f HZViewController.m
Breakpoint 8: where = HZTest_Example`-[HZViewController init] + 20 at HZViewController.m:35, address = 0x000000010f3c2ba4
(lldb) br list
Current breakpoints:
8: file = 'HZViewController.m', line = 35, exact_match = 0, locations = 1, resolved = 1, hit count = 0
8.1: where = HZTest_Example`-[HZViewController init] + 20 at HZViewController.m:35, address = 0x000000010f3c2ba4, resolved, hit count = 0

Note:如果指定的地方是一个属性,那么就会指定其set和get方法,如果指定的地方是一个空白行的地方,则自动向下寻找第一个存在函数的指定行

后面其实都是给前面的命令,新增一些功能而准备的。就不一个个去设置了。

  • -c 用来设置断点条件,比如说我们有时会希望想当一个条件为真的时候,才触发断点操作,那么我们就可以去使用这个东西,对应如果是使用UI去设置的话就是这个选项框

    1
    br set -n setString: -c string == @"TestString"

    IMAGE

  • -o 来设置单次断点WWDC 2018 Session412中介绍的一点
    -o=–one-shot true
    配合br command add可以达到在某个断点后单次拦截某个函数,有兴趣看怎么操作的可以去看一下这位写的效率提升爆表的 Xcode 和 LLDB 调试技巧
  • -i 设置执行忽略断点次数,对应的UI设置如图所示。
    IMAGE
函数 区别
br set -f Test.m –line 12 给某个文件的12行函数加个断点
br set –name test 给所有test 函数加个断点
br set –method test 给所有test C++函数加个断点
br set –selector test 给所有test OC函数加个断点
br set –shlib Foundation.framework –name test 给Foundation库的test函数加个断点

br command

从上面的set中,我们知道了如何去用代码去设置一个断点。当我们去执行了断点后,我们希望让他去自动的去帮我们去输出一些内容,而不是我们一个个po/e去打印。那么这个command就是为了这个而设计的。从可视化的方式设置的话,就如图所示了。
IMAGE

Note: 其实Xcode提供了很多种类断点后执行的方式,比如执行一段脚本/发出声音/打印信息等等,我们这边只讲打印Debug信息的内容,跟你在LLDB中打印无异。

br command list 显示某个断点当前的所有可执行项

1
2
3
(lldb) br command list 2
Breakpoint 2:
po self

br command add 给断点添加一个可执行项

1
2
3
4
5
6
(lldb) br set -n testGlobalPoint -f HZViewController.m
Breakpoint 3: where = HZTest_Example`-[HZViewController testGlobalPoint] + 23 at HZViewController.m:52, address = 0x0000000105396d27
(lldb) br command add -o "po self" 3
(lldb) br command list 2
Breakpoint 2:
po self

Note: 如果你尝试给一个断点添加多个命令,-o是无法满足的,因为-o是–one-line的意思,他只能添加一行。后续的添加会覆盖前面的。
所以你可以直接使用br command add 3 (3是断点标识)
当然你可以去给断点加个脚本help br command add。可以看到Apple给我们举了一个添加脚本的🌰。虽然我并不知道这玩意儿有啥鸟用
小技巧LLDB中有个关键词是contiune,我们可以键入这个去跳过断点,所以我们输入多行的时候就可以这么玩了,这样会跳过断点,但是又会有合理的Log输出

1
2
3
4
5
(lldb) br command add 3
Enter your debugger command(s). Type 'DONE' to end.
> po self
> continue
> DONE

br command delete 删除一个断点的可执行项

1
2
3
(lldb) br command delete 3
(lldb) br command list 3
Breakpoint 3 does not have an associated command.

br list

查看当前设置的断点的列表,enable/disable的都会显示在上面,包含运行期的代码断点和运行前的可视化断点
LLDB会默认先读取你在非运行期,设置的断点即在Xcode的函数左边的小箭头,当我们每次设置一次可视化的断点。就会去设置断点文件。
文件路径/工程目录//Users/hongzhizhao/Desktop/HZTest/Example/HZTest.xcworkspace/xcuserdata/hongzhizhao.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

1
2
3
4
5
6
7
8
9
(lldb) br list
Current breakpoints:
1: file = '/Users/xxxxx/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 44, exact_match = 0, locations = 1, resolved = 1, hit count = 0
1.1: where = HZTest_Example`-[HZViewController testGlobalPoint] + 23 at HZViewController.m:44, address = 0x0000000100d42d37, resolved, hit count = 0
2: file = '/Users/xxxx/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 37, exact_match = 0, locations = 1, resolved = 1, hit count = 1
2.1: where = HZTest_Example`-[HZViewController viewDidLoad] + 61 at HZViewController.m:37, address = 0x0000000100d42c6d, resolved, hit count = 1

br delete

顾名思义,删除断点。我们可以通过br list中去找到对应的nuber。(包含二级的也可以。)

1
breakpoint delete #number#

1
2
3
4
5
6
7
8
9
10
11
12
(lldb) br set -n init
Breakpoint 6: 3657 locations.
(lldb) br list
Current breakpoints:
6: name = 'init', locations = 3657, resolved = 3657, hit count = 0
6.3656: where = EmailCore`-[ECEncodedWordEncoder init], address = 0x000000011e6caa14, resolved, hit count = 0
6.3657: where = CoreRecents`-[CRRecentContactsLibrary init], address = 0x000000011e6dec27, unresolved, hit count = 0
(lldb) br delete 6.3657
0 breakpoints deleted; 1 breakpoint locations disabled.
(lldb) br list
6.3656: where = EmailCore`-[ECEncodedWordEncoder init], address = 0x000000011e6caa14, resolved, hit count = 0
6.3657: where = CoreRecents`-[CRRecentContactsLibrary init], address = 0x000000011e6dec27, unresolved, hit count = 0 Options: disabled

br disable/enable

有些时候,我们可能根据具体场景设置了一些断点,但是临时开发的时候我们却不想让其进入断点。OK,我们可以去临时改写这个断点的状态为disable,当我们希望其开启的时候再设置为enable就可以了~,perfect?

1
2
3
4
5
6
7
8
9
10
(lldb) br list
Current breakpoints:
1: file = '/Users/hongzhizhao/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 44, exact_match = 0, locations = 1, resolved = 1, hit count = 1
1.1: where = HZTest_Example`-[HZViewController viewDidLoad] + 61 at HZViewController.m:44, address = 0x0000000105396c1d, resolved, hit count = 1
(lldb) br disable 1
1 breakpoints disabled.
(lldb) br list
Current breakpoints:
1: file = '/Users/hongzhizhao/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 44, exact_match = 0, locations = 1 Options: disabled
1.1: where = HZTest_Example`-[HZViewController viewDidLoad] + 61 at HZViewController.m:44, address = 0x0000000105396c1d, unresolved, hit count = 1

br clear

清理指定文件的指定行的断点

1
breakpoint clear -l #lines# -f #filename#

下面命令为

  • 查看当前list确实存在一个断点
  • 使用clear命令清除name为5的断点
  • 再次查看当前list,name为5确实被移除了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    (lldb) br list
    Current breakpoints:
    5: file = '/Users/hongzhizhao/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 48, exact_match = 0, locations = 1, resolved = 1, hit count = 0
    5.1: where = HZTest_Example`-[HZViewController testGlobalPoint] + 23 at HZViewController.m:48, address = 0x000000010da39d37, resolved, hit count = 0
    (lldb) br clear -l 48 -f "HZViewController.m"
    1 breakpoints cleared:
    5: file = '/Users/hongzhizhao/Desktop/HZTest/Example/HZTest/HZViewController.m', line = 48, exact_match = 0, locations = 1, resolved = 1, hit count = 0
    (lldb) br list
    Current breakpoints:

br read/write

我们前面讲list的时候,我们讲到过一些存储断点相关的东西,br write -f #file_path#,-f标识断点文件的存储路径。
前面说了这么多的断点设置,其实都是运行期的设置,出了运行期,这些设置都是没有了的。所以read/write是一个持久保存的方案。

1
2
3
4
5
6
(lldb) br write -f ~/Desktop/a.log
(lldb) br read -f ~/Desktop/a.log
New breakpoints:
Breakpoint 4: where = HZTest_Example`-[HZViewController viewDidLoad] + 61 at HZViewController.m:44, address = 0x0000000105396c1d
Breakpoint 5: where = HZTest_Example`-[HZAppDelegate application:didFinishLaunchingWithOptions:] + 73 at HZAppDelegate.m:16, address = 0x0000000105396719
Breakpoint 6: where = HZTest_Example`-[HZViewController testGlobalPoint] + 23 at HZViewController.m:52, address = 0x0000000105396d27

Note: read并不能读取Xcode使用可视化设置的埋点,可能跟版本有关系吧,可视化埋的断点,是V2版本的。但是使用命令的并没有版本标识,可视化埋点是xml,使用命令保存的却是json文件。另外read并是一个会去替换的更新,而是一个读取后全部添加~

属性监听 watchpoint

OK,我们看到了如何去设置一个函数的断点。但是有些时候,我们可能并不满足于函数,需要去监听一些属性的变化。那么有些人就会说,我直接监听set方法不就好了?那么就会有两种情况不会过set方法,1.直接调用的是_ 2.这个属性压根就没有getter和setter方式是一个纯只指针的访问形式呢?你监听给我看看?
所以watchpoint就是这么一个神奇的东西,说白了他是监听一个内存地址的变化,所以他能够去捕获变量的变化。

watchpoint 跟breakpoint的命令大致相同,只是一个操作函数,一个操作地址罢了

watchpoint set

watchpoint set variable
跟设置函数一样的去设置一个断点

1
2
3
4
5
6
(lldb) watchpoint set variable self.window
error: "self" is a pointer and . was used to attempt to access "window". Did you mean "self->window"?
(lldb) watchpoint set variable self->_window
Watchpoint created: Watchpoint 1: addr = 0x600000ccc110 size = 8 state = enabled type = w
watchpoint spec = 'self->_window'
new value: 0x00007fc8b780d600

由于watchpoint是一个内存块的监听策略,如果我们去写self.window这种表达式,实质是在设置[self setWindow:]方法,那么跟他的定义就大相径庭了。这边举个例子

watchpoint set expression

1
2
3
(lldb) watchpoint set expression 0x00007fc8b780d600
Watchpoint created: Watchpoint 2: addr = 0x7fc8b780d600 size = 8 state = enabled type = w
new value: 4440870480

-w统一参数
read/write/read_write
从字面意思上,我们就可以看出是监听的内容是读还是写还是读写一起监听。默认是写操作

variable/expression的区别应用
variable必须是你进入了某次断点,才能够用使用当前栈中使用的属性去监听
expression 你可以通过一些逆向软件来计算偏移量+首地址的形式去得到最终的内存地址

watchpoint list

这个我们就不多说了,查看当前的watchpoint列表

1
2
3
4
5
6
7
8
(lldb) watchpoint list
Number of supported hardware watchpoints: 4
Current watchpoints:
Watchpoint 1: addr = 0x600000ccc110 size = 8 state = enabled type = w
watchpoint spec = 'self->_window'
new value: 0x00007fc8b780d600
Watchpoint 2: addr = 0x7fc8b780d600 size = 8 state = enabled type = w
new value: 4440870480

这边我们看到使用expression和variable会得到两条结果

watchpoint delete

删除一个监测点

1
2
(lldb) watch delete 1
1 watchpoints deleted.

watchpoint disable/enable

跟breakpoint一样,他也有一个临时关闭和打开的设置开关

1
2
3
4
(lldb) watchpoint disable 3
1 watchpoints disabled.
(lldb) watchpoint enable 3
1 watchpoints enabled.

watchpoint ignore

一个断点在真正被执行前需要被断点几次?

1
2
(lldb) watchpoint ignore -i 3 3
1 watchpoints ignored.

watchpoint modify

当条件为真的时候,采取真正执行断点

1
2
(lldb) watchpoint modify -c "self ==nil" 3
1 watchpoints modified.

watchpoint command

这个跟breakpoint command 没有任何区别,这边就不去说这个东西了。

控制程序 thread

说完了如何去调试你的代码,但是有的时候这往往是不够的。我们希望于去控制我们的程序走向。例如一个函数的返回值,会改变程序的走向,那么我希望去改变他的返回值。这个时候我们就可以用到thread这个命令来辅佐我们。我们日常用的step调试,其实都是thread命令,只是Xcode帮我们可视化了罢了。

thread return

return顾名思义,直接使函数return,return后面可以接表达式,来帮助我们返回一些有利于我们调试的东西

1
thread return [<expr>]

1
2
3
4
5
6
7
8
9
- (void)testReturn {
NSString *a = [self testReturn] ? @"a" : @"b";
}
- (BOOL)testReturn {
return true;
}
(lldb) thread return false
(lldb) po a
b

thread step-x

我们平时的单步调试,其实都来自于这个命令
IMAGE
|命令|作用|
|–|–|
| thread countinue/c/countinue |等同于第一个按钮,标识程序继续运行|
| thread step-over/n/next | 等同于第二个按钮,单步运行 |
| thread step-in/s/step | 等同于第三个按钮,进入函数方法内部 源码级别|
| thread step-out/finish | 等同于第四个按钮,跳出当前函数 |

thread jump

OK,这玩意儿真的是一个黑魔法。看WWDC2018,断点移动的我一愣一愣的。来感受一下?
未命名.gif
其实这个东西很简单,老早就有了,只是我不知道罢了。
thread jump -l 上面的图是可视化的罢了,这个才是实质代码。-l 输入跳转的代码。

Note:且用且珍惜,这东西有利有弊

其他东西

  • thread list 列出当前所有的线程
  • thread select 选择某个线程
  • therad until 传入一个行数,让程序执行到这行的时候暂停
  • thread info 输出当前线程的信息
  • thread backtrace 断点的函数调用堆栈,默认当前线程
  • thread plan 用来管理当前线程的执行计划

thread命令虽好,但是随意改变成语的走向,可能会引起一些不必要的麻烦

查看调用栈 frame

在我们调试过程中,我们经常能看到,控制台帮我们输出了当前的调用栈内容。其实他对应的就是frame命令。
IMAGE
细心的同学可能会看到一个细节,红框1和红框2是对应的,红框2是用thread backtrace打印出来的。里面的frame其实就是我们这面的frame,我们可以通过frame select 来使用代码切换当前运行的调用栈,跟你手动点现况1的没有任何区别
IMAGE
frame variable用来打印当前调用栈内的所有属性

1
2
3
4
(lldb) frame variable
(HZViewController *) self = 0x00007f8c6540ba90
(SEL) _cmd = "viewDidLoad"
(__NSCFConstantString *) a = 0x000000010fffa128 @"b"

frame info打印当前栈的内容

1
2
(lldb) frame info
frame #0: 0x000000010fff7c64 HZTest_Example`-[HZViewController viewDidLoad](self=0x00007f8c6540ba90, _cmd="viewDidLoad") at HZViewController.m:52

help/apropos

当然说了这么多,很多时候我们肯定会忘掉的~啧啧啧,谁还能记住这么多呢,毕竟Xcode帮我们做好了可视化这么好的东西。
但是有些时候,我们却不得不去找这些命令,比如逆向的时候。2333333
help很简单,帮助我们去打印描述信息的。例如我想知道frame的相关信息,我们就可以这么操作,里面都帮我们写好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(lldb) help frame
Commands for selecting and examing the current thread's stack frames.
Syntax: frame <subcommand> [<subcommand-options>]
The following subcommands are supported:
info -- List information about the current stack frame in the current
thread.
select -- Select the current stack frame by index from within the
current thread (see 'thread backtrace'.)
variable -- Show variables for the current stack frame. Defaults to all
arguments and local variables in scope. Names of argument,
local, file static and file global variables can be
specified. Children of aggregate variables can be specified
such as 'var->child.x'.
For more help on any particular subcommand, type 'help <command> <subcommand>'.

apropos,是一个搜索命令,帮我们查找有哪些命令可以使用,例如下图这样,我们就能够答应出关于step的所有命令来帮助我们来使用

1
2
3
4
5
6
7
8
9
10
11
12
13
(lldb) apropos step
The following commands may relate to 'step':
thread step-in -- Source level single step, stepping into calls.
Defaults to current thread unless specified.
thread step-inst -- Instruction level single step, stepping into calls.
Defaults to current thread unless specified.
thread step-inst-over -- Instruction level single step, stepping over calls.
Defaults to current thread unless specified.
thread step-out -- Finish executing the current stack frame and stop
after returning. Defaults to current thread unless
specified.
thread step-over -- Source level single step, stepping over calls.
Defaults to current thread unless specified.

Extension

刷新UI

即使你改了UI的属性,因为程序暂停,你是无法刷新UI的,但是有个命令可以帮你,这样做你就可以及时的帮助你来改变UI的刷新,即使他在断点的状态

1
e CATransaction.flush()

使用别人写好的脚本

LLDB是支持使用别人写好的命令的,实质语法是使用python写的,这边就不多做介绍了。有兴趣可以去了解一下

别名功能

很多时候命令很长,我们记不住怎么办。比如那个刷新UI的命令。好长啊,在Session里面,这个人有个恶趣味用🚽来代替那个命令。

1
command alias 🚽 e CATransaction.flush()

参考

iOS 常用的lldb命令
使用 LLDB 调试 APP
熟练使用 LLDB,让你调试事半功倍