LLDB 是 Xcode 中的默认调试器,支持调试 C、Objective-C、C++,用 LLDB 调试代码的好处不必多说,基本 Xcode 中常见的调试方式 LLDB 都支持,但 LLDB 还可以在运行时写和运行一些简单代码、运行 Python 代码来扩展调试的方式。
现在让我们来总结写 LLDB 都有哪些调试方式吧~
打印
我们最常用的可能就是进行打印操作,在打下断点,程序暂时停止,在 console 中写命令打印一些变量的值:
- po oc对象
会调用oc对象的description方法,将这个对象打印出来 - p 基本数据类型
可以使用 print、或其简写 p 打印一些基本数据类型
Help
- help 命令
下面讲的所有命令都可以通过 help 查看其具体含义、信息
Breakpoint
可以直接用写命令的方式打断点、调试、编辑断点信息:
b 某文件.m:30
给 某文件的第30行打下一个断点br l
列出当前工程的所有断点信息br delete n
删除第n个断点br enable n
使第n个断点有效br disable n
使第n个断点失效br set -n 某方法
设置关于这个方法的符号断点,在调用这个方式时,程序都会暂停br mod -c "某条件" n
设置条件断点,给第n个断点加一个条件
在断点停下后,还可以像按这些键一样,调试断点:
- c
Continue,继续执行 - n
StepOver,一步一步执行方法 - s
StepInto,进入方法调用里面 - finish
StepOut,跳出方法调用
Expression
expr + OC 代码,在运行时可以执行,对于输出一些运行时才会确定的变量十分有用。
- expr (void) NSLog(@"balhbalh")
简单的打印操作 -
expr + 结构体
expr 可以打印结构体的值
-
expr + 变量赋值
这个会实际改变变量的值
- expr + 方法调用
最神奇的莫过于这个,在运行时直接调用指定方法,给运行时加一些“行为”,比如
给断点添加了一个行为,执行 expr 后面的方法,然后 continue。
这样每次在ViewController出现时,会执行这个 segue ,推出后面的 Controller,如图:
Backtrace
- bt
当程序 crash 掉时,可以用 bt 查看程序运行时函数调用的堆栈信息 - bt all
bt 显示的是当前线程函数调用的信息,可以加 all 显示所有线程的堆栈信息
Thread
可以操作线程,显示当前线程信息,在运行时,直接改变方法调用的返回值
- thread list
显示所有线程信息 - thread select n
选择第n个线程 - thread backtrace
显示当前 thread 的堆栈信息 - thread backtrace all
显示所有 thread 的堆栈信息 - thread until i
使线程运行,并在第 i 行时停下 -
thread return 返回值
直接改变当前方法调用的返回值
Frame
运行时直接查看变量的值
- frame variable
如上图,frame variable 直接查看当前变量的值 - frame variable 变量名
查看指定变量名的值
Watchpoint
当变量变化时,程序暂停,显示 watchpoint 的变化
- watchpoint list
显示当前所有 watchpoint - watchpoint set variable 变量名
给变量设置 watchpoint - watchpoint modify -c "条件" n
给第n个断点添加条件,只有满足条件时,watchpoint 才会触发 - watchpoint modify -c "" n
条件置空时,即为给这个 watchpoint 删除条件 - watchpoint set expression -- 内存地址
对任意内存地址进行观察
Script
lldb 内置了 Python 的脚本解释器,可以解释运行 Python 代码
- breakpoint command add -s python n
给第n个断点添加python代码
- breakpoint command add -F "python文件名"."python方法名" n
给第n个断点添加导入的 python 文件中的方法调用
Command
- command script import "文件路径/文件名"
导入已有的脚本文件,在调试时可以直接用这个文件 - command import "文件路径/文件名"
导入已有的 lldb 调试脚本文件 - command unalias pf
删除用户自定义的 aliases - command history
打印在当前运行状态下,lldb 调试时command 历史
示例:
- 新建一个 empty 文件,文件后缀名为 py,内容为:
def print_locals(frame, bp_loc, internal_dict):
variables = frame.GetVariables(False,True,False,True)
for i in range(0, variables.GetSize()):
variable = variables.GetValueAtIndex(i)
print variable
大概意思为,定义一个方法,会将当前的变量一个一个打印下来
frame 指当前堆栈信息,bp_loc 指断点的具体位置,internal_dict 这个python 文件的一些参数信息
2.导入 python 代码
3.给断点加入 python 代码中定义的方法
如图所示:
Alias
当遇到较为复杂的命令,可以给这些命令定义一个别名,每次调用别名即可。
- command alias "别名" 具体命令
给具体命令设置一个别名
上面的方法存在一个问题,设置的别名只在当前运行过程有效,下次就失效了,怎么持久化呢?
可以将这些命令封装起来,保存在电脑中,每次调试时都可使用。
-
1.新建 lldbinit 文件,在终端中输入
$ touch ~/.lldbinit
$ open ~/.lldbinit
2.重启Xcode,再次编译运行
3.打断点,并输入 pf
Chisel 是 Facebook 开源的一款 lldb 命令集合文件,GitHub 地址。我们可以直接使用一些定义好的 lldb 命令来调试程序。
安装十分简单,确保 Mac 已经安装了 homebrew 环境
$ brew update
$ brew install chisel
安装成功后会出现
并且提示要将框中出现的命令加在刚刚生成的那个 .lldbint 文件中。
重启 Xcode 之后,每次就可以使用这些命令了。
在 lldb 中输入 help,我们发现最下面出现了一些用户自定义命令,这就是 Chisel 中的。
比较常用的有border,给视图加边框;hide,隐藏视图;mask,给视图加遮罩;presponder,显示响应者链……
关于 Xcode 调试的文章先总结到这里,希望以后能继续补充完善🤖
参考文章
Xcode LLDB Tutorial
The LLDB Debugger
Dancing in the Debugger — A Waltz with LLDB