Xcode 调试技巧

我怕爱的太早我们不能终老 提交于 2020-03-02 12:39:33

  【前言】:本篇为同事崔桂祥分享资料。

  随着Xcode 5的发布,LLDB调试器已经取代了GDB,成为了Xcode工程中默认的调试器。它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。LLDB为Xcode提供了底层调试环境,其中包括内嵌在Xcode IDE中的位于调试区域的控制面板,在这里我们可以直接调用LLDB命令,示例如下:

1.必备篇

1.1 打印变量:print/po

  • print:打印变量的值可以使用print命令,该命令如果打印的是简单类型,则会列出简单类型的类型和值。如果是对象,还会打印出对象指针地址;
  • print object:如果我们只想查看对象的值的信息,则可以使用po(print object的缩写)命令。

1.2 查看线程状态:thread list 

  在进程停止后,LLDB会选择一个当前线程和线程中当前帧(frame)。很多检测状态的命令可以用于这个线程或帧。
  为了检测进程的当前状态,使用该命令,其中星号(*)表示thread #1为当前线程。

1.3 获取线程的跟踪栈:thread backtrace(简写bt)

  使用命令thread backtrace(简写bt)可以查看线程的跟踪栈,若要查看所有线程的调用栈则可以使用命令:thread backtrace all (简写bt all)

1.4 列出帧参数和本地变量:frame variable 

  此处frame代指线程中当前帧(frame)

1.5 寻址:image

  image 我们可以用它来查找可执行文件或共享库的原始地址,当我们的程序崩溃时,我们可以使用这条命令来查找崩溃所在的具体位置,如下所示:

  这段代码在运行后会抛出如下异常:

  现在,我们怀疑出错的地址是0x000000010ce22e42(可以根据执行文件名判断,或者最小的栈地址)。为了进一步精确定位,我们可以输入以下的命令:image lookup --address

  可以看到,出错的位置是 UserListViewController.m 的第206行

1.6 帮助系统:help

  LLDB帮助系统让我们可以了解LLDB提供了哪些功能,并可以查看LLDB命令结构的详细信息。熟悉帮助系统可以让我们访问帮助系统中中命令文档。

  我们可以简单地调用help命令来列出LLDB所有的顶层命令。

  如果help后面跟着某个特定的命令,则会列出该命令相关的所有信息,如下所示:

2.技巧篇

2.1 运行时修改变量的值:expression

  以前怎么验证是不是某个变量的值导致整段程序不能正常工作?修改代码中的变量的值,然后cmd+r重新启动app?现在你不需要这么做了,只需要设置一个断点,当程序在这进入调试模式后

使用 expression 命令即可在运行时修改变量的值

  命令执行完毕,继续运行应用,会发现header背景颜色变为黄色。该技巧实用于debug时改变某个label文字内容多少,查看显示多行/单行效果。

2.2 异常排查:异常断点

  如果添加了异常断点,当程序每次发生了异常,都会被中断。一般用来捕获未知异常,运行效果如下

2.3 符号断点

  symbolic breakpoint使用很简单,点击断点界面的“+”号,选择”Add Symbolic Breakpoint”就创建了一个断点:

  下面开始最重要部分,断点的可编辑项,如图:

  从编辑界面可以看到断点可编辑的项有Symbol、Module、Condition、Ignore、Action、Options

  • Symbol

  断点触发函数。有两种函数写法,一种是C函数样式,一种是OC方法样式。 
  C函数样式只需要写函数名,不用写后面的()和参数。例如NSLog。 
  OC方法样式的[className methodName] className是类名,methodName是方法名(不区分类方法和实例方法)。如果写标记的这个类的方法被子类重写了则子类的方法也会触发断点。例如[UIViewController viewDidLoad]。

  • Module

  模块筛选。可以避免不同库中方法名或者函数名相同。

  • Condition

  触发条件。这里可以添加一些指定触发条件,比如添加第一个参数不能为nil。这里$arg3代表第1个参数,$arg4代表第2个参数,以此类推。这里也可以调用方法来判断,必须是类方法,并且返回值必须为BOOL类型 

  示例:找出给[UIImage imageNamed:]传nil的代码。这里就需要设置Symbol为[UIImage imageNamed:],然后Condition设置为$arg3 == nil。这样在运行中如果遇到传nil就会触发断点。

  • (4)Ignore

  触发开始次数。设置这个值可以忽略前面指定次数的触发。

  • (5)Action

  触发活动。这里是当断点触发后要执行的动作,可以添加多条,执行的顺序是从上到下。一共有6种可执行类型,如下图

  平时主要使用以下几种:

  1. Debugger Command:会在断点触发的时候执行LLDB命令。可以打印对象、修改对象值等功能。
  2. Log Message:会在断点触发的时候打印日志。其中@exp@打印对象值,exp为对象名;%B表示断点名;%H表示当前断点触发的次数。
  3. Shell Command:会在断点触发的时候执行Shell命令。
  • Options

  是否进入DEBUG界面。勾选这个断点触发后不进入DEBUG界面,断点打印日志或者声音断点一般都勾选。有时候我们的程序不知道跑到哪个地方就 crash 了,而 crash 又很难重现。保守的做法是在系统抛出异常之前设置断点,具体来说是在 objc_exception_throw处设置断点。这样在 Debug 模式下,如果程序即将抛出异常,就能在抛出异常处中断了。效果类似Exception breakpoint。

2.4 Watchpoints

  Watuchpoints是一个用来监听变量的值的变化或者内存地址的变化的工具,发生变化时会在debugger中触发一个暂停。对于那些不知道如何准确跟踪的状态问题,可以利用这个工具来解决。要设置watchpoint的话,在程序运行到stack frame包含有你想观察的变量时,让debugger暂停运行,这个时候变量在当前stack frame的scope内,这个时候才能对该变量设置watchpoint。

  你可以在Xcode的GUI中设置watchpoint,在xcode的 Variables View中,把你想观察的变量保留出来,然后右键设置“Watch XXX”。例如下图,观察self的title变量,点击 Watch “_button1ClickCount” 即可。

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!