iOS开发中的常用调试工具(一): LLDB

1,356 阅读7分钟

日常开发中我们经常使用断点来帮助我们调试解决问题, 但其实断点只是LLDB的一小部分而已, 除此之外还有许多更强大的隐藏功能可以帮助我们进行开发中的调试.

什么是LLDB

LLDB的全称是Low level Debug, 是Xcode内置为我们提供的强大调试工具, 当程序运行到断点处, LLDB就会出现在控制台中, 能帮助我们进行更加丰富的流程控制和数据检测. 它的出现简直就是iOS开发者的福音啊!

LLDB的常见使用

下面我会为读者们介绍LLDB的几种常见使用, 相信以前没有认真学习过LLDB的读者们在读完之后都会对LLDB的强大感到深深的敬佩了. 话不多说, 接下就进入文章的正题.

断点设置

1. 使用LLDB给方法下断点

LLDB调用指令:

breakpoint set -n "[class selector]"

参数说明:

  • class: 表面要设置断点方法的调用类
  • selector: 表明要设置断点的方法

具体使用:

接下来我给大家展示一下如何给多个方法同时设置断点. 具体为在LLDB中调用breakpoint set -n "[Viewcontroller test1]" -n "[Viewcontroller test2]" -n "[Viewcontroller test3]" 指令,

可以看到当我们输入完指令后, 打印台打印出了Breakpoint 3: 3 locations, 告诉我们断点3在三个位置设置成功了.

我们还可以输入breakpoint list查看已经设置所有断点的具体信息.输入之后我们可以看到刚刚设置的三个断点的具体信息如位于那个文件, 位于哪个方法, 方法地址等信息.

示例中我给大家展示的设置多个端点, 单个断点也是一样的步骤.

2. 使用LLDB使断点失效

​ LLDB调用指令:

  • 禁用单个断点: breakpoint disable 3.1
  • 禁用一组断点: breakpoint disable 3

参数说明:

  • 最后的数字代表要失效的断点序号, 可以通过breakpoint List查看断点序号. 注意此处的序号可以是整一组, 也可以是一组里的单独某1个断点比如3.1

具体使用:

​ 设置一组断点

设置一组里特定一个断点

可以看到第一张图我们是给整一组断点设置了失效, 所以三个位置的断点都是disable状态. 而第二张图可以看到只有第三组里的第一个位置的断点是失效的,

3. 使用LLDB启用断点

LLDB调用指令:

  • 启用单个断点: breakpoint enbale 3.1
  • 启用一组断点: breakpoint enable 3

具体使用:

4. 使用LLDB删除断点

​ LLDB调用指令:

  • 删除单个断点: breakpoint delete 3.1

  • 删除一组断点: breakpoint delete 3

    具体使用:

    删除一组断点:

    删除一组中特定一个断点

通过查看断点列表我们可以看到实际上我们是无法删除单个断点的, 如果调用了删除单个断点指令也只是将其禁用, 并没有实际上被删除掉.

5. 使用LLDB给工程中所有同一名字的方法设置断点

​ LLDB调用指令:

breakpoint set --selector X

参数说明:

X代表你要设置的方法名

具体使用:

通过打印台可以看到处于两个文件里的同一名字的方法都被设置上断点了.

6. 使用LLDB给特定文件里的方法设置断点

​ LLDB调用指令:

breakpoint set --file A --selector B

参数说明:

  • A代表你要设置断点所在的文件名

  • B代表你要设置断点所在的方法名

    具体使用:

这种用法和第一种用法的效果是一样的, 大家根据喜好选择.

7. 使用LLDB给工程中所有包含某一字符串的所有方法设置断点

​ LLDB调用指令:

breakpoint set -r A

参数说明:

  • A代表方法包含的字符串

    具体使用:

可以看到设置上了许多断点, 所有包含关键字的方法都会被设置上断点, 包括系统库.

8. 断点设置的缩写

觉得指令有点长, 想偷懒的朋友们可以采用下面的缩写快速打出指令:

b = breakpoint set

-f = file

-n = --name

LLDB进阶使用

1. p和po指令

我们经常在LLDB调试中输入 p xxxpo xxxx, 那么这里的p 和 po 到底是什么含义呢?

在LLDB中输入help phelp po 来查看相关说明:

  • p xxx 代表在当前线程执行xxx表达式。
  • po xxx 代表执行xxx的description方法,输出变量有关的信息. 我们可以通过重写对象的description方法在调试打印出一些想要的信息如对象地址, 容器对象的所包含的对象信息等等.

现在我们用p在调试时改变View的frame. 在LLDB中输入 p self.testView.frame = CGRectMake(100, 100, 100, 100); 回车之后就可以看到vioew的frame发生改变.

这个功能使得我们可以在调试时动态执行某些方法测试运行效果, 并且对下一次运行不会产生影响.

再来看看po指令的使用.我们首先重写自定义View的description方法, 返回当前类所属父类.然后在LLDB中输入 po self.testView 看看效果.

重写description方法:

打印台输出信息:

可以看到我们正确输出了父类的信息, 之后若想要在一些特定类调试时输出额外信息就可以使用上述方法.

2. 与函数调用栈有关指令

在程序运行崩溃时我们可以看到打印台输出了一长串信息的, 其中我们能看到有关崩溃时调用方法的函数栈, 通过查看函数调用栈我们可以定位到底是哪个方法出了问题.

在LLDB中我们可以主动查看执行到当前断点的方法调用栈. 输入指令bt就可以查看了.

可以看到函数调用栈里包含了从系统库调用的方法到当前断点调用的方法经历的所有方法, 除此之外还显示当前调用的线程. 通过这个功能可以快速定位特定方法的调用过程.

我们还可以通过updown切换到栈中的上一个方法和下一个方法,

除此之外想要跳到特定位置的方法, 就输入 frame select x 其中x代表对于的方法的序号, 这个序号可以在刚开始的bt指令进行查看

我们还可以通过 frame variable 查看当前方法的所有参数:

可以看到除了自己定义的参数还打印出了系统默认为我们设置的self参数, _cmd参数. _cmd参数代表当前方法名.

3. 流程控制指令

有时候我们可能在调试中需要一步步执行函数, 然后在每个特定时机进行各种LLDB调试. 这时候就需要LLDB的流程控制指令了.

LLDB常用的两个流程控制指令为ns. 那么两者有什么区别呢?

n指令会把子函数当作做整体一步执行, 而s指令遇到子函数就会进入.

其实功能相当于控制台上方两个按钮的功能

总结

LLDB为我们提供了强大的调试功能包括断点的操, p执行特定表达式, po输出对象的具体信息, 函数调用栈的查看, 调用流程控制等功能. 下次我们在开发调试中就可以运用这些功能帮助我们更高效的测试程序. 最后, 除了上面介绍的功能大家还可以通过help指令查看LLDB更多用关的用法.