Xcode调试技巧和窍门— WWDC 2018

调试是整个软件开发过程不可或缺的一部分。 一个好的程序员应该具有高效的调试技能,以便从长远来看编写无错误的代码。

通过本文,我们将熟悉通过LLDB命令进行的Xcode调试,从而避免了每次修改都需要重新编译代码的麻烦。

提示1: 表达命令

有效地利用expression命令并以根本不需要重新编译代码的方式配置断点。

如果要在命中断点时撤消任何代码,请在Xcode控制台中使用expression命令。 在Xcode控制台中,在下面的命令中输入要执行的代码。

 表达式“要评估的代码行” 

断点也可以配置为通过在编辑断点选项卡内添加相同的表达式命令来自动完成。

使用expression命令在调试之间插入代码。 在下面的示例中,文本的原始值是“商业新闻版块”。 在控制台中使用expression命令可以修改文本字符串。

什么时候使用?

如果需要查看更复杂的数据或更改程序数据,则可以使用常规的“表达式”命令。 它在调试时接受一个表达式,并在当前选定帧的范围内对其求值。

提示2:符号断点

符号断点非常强大,可以将其添加到名称与断点符号匹配的任何方法中。 开发人员无需担心方法的签名或方法所属的模块。 如下例所示,在断点的符号字段中添加要为其触发断点的方法名称。

当[UILable setText:]之类的目标C格式方法在符号断点中添加其名称时被触发。 在下面的示例中,该行触发了断点。

  cell.title.text = getTextForEachSection 

在这种情况下,当遇到断点时,我们将开始查看汇编代码,因为我们没有UIKitCore的源代码。

如果您使用的是系统框架的汇编代码,则不必担心,因为我们可以使用伪寄存器检查传递给setText函数的参数。 伪-调试器提供的寄存器可以查看寄存器保存的参数。

  • $ arg1 —我们可以看到目标c消息的接收者
  • (SEL)$ arg2 —选择器。 (SEL)是必需的,因为LLDB不知道这些参数的类型。
  • $ arg3 :第一个参数传递给函数

什么时候使用?

修复控制台中的自动布局警告(如UIViewAlertForUnsatisfiableConstraints)和布局相关的警告(如UICollectionViewFlowLayoutBreakForInvalidSizes)。

提示3:向断点添加条件

当更多的对象具有与断点匹配的符号时,可能会出现频繁击中符号断点的情况。 我们可以通过添加一个仅在满足断点条件时才触发断点的条件来避免这种情况。

秘诀4:一杆断点

一个临时断点,仅在其触发之前存在,然后将其自动删除。 在击中一个击球断点后,可以使用它激活任何其他断点。

 断点集-一键式true --name“-[UILabel setText:]” 

在上面的示例中,仅在击中一个镜头断点后才激活符号断点“ setText”。

提示5:跳过代码行的执行以简化调试。

在许多情况下,我们希望通过跳过一些代码来节省调试时间。 我们可以通过要求调试器跳过代码来做到这一点。

  • 将抓取手柄拖动到断点线程1(指令指针)附近,到您感兴趣的代码或
  • 通过添加表达式将断点配置为跳过代码行
 线程跳转--by(要跳过的行数) 

提示6:为任何对象打印自定义调试描述。

我们可以使用CustomDebugConvertible协议为任何类对象自定义调试描述。

提示7:观看断点

设置变量的值时,它用于监视或监视任何变量。

在上面的示例中,我们在人员年龄变量上设置了监视中断点。 只要在代码中设置了年龄变量,监视断点就会被触发。


在进行布局和约束调试之前,首先要介绍以下几点

目标C模式命令

命令别名

不安全的比特广播

定制调试文件

目标C模式命令

有些API并非很快公开,只能用于调试过程。 目标C是一种动态语言,可以在目标C模式下调用这些API。

假设您有一个简单的视图层次结构,并且需要为视图打印调试描述。 这是Xcode可视3D模式调试的替代方法。

  • expression -l objc —通知调试器表达式即使在快速帧上也应在目标c模式下求值。
  • O —与po相同,以打印描述。
  • [‘self.view’recursiveDescription]-原始表达式输入。

命令别名

可以说,某些命令经常被使用,我们可以通过别名来轻松键入它们。

不安全的比特广播

如果对象的内存地址已知,则获取该对象的调试描述的另一种方法。 这是不安全的,因为要由您提供正确的对象类型。

unsafeBitcast(x:T,to:U.type)

关于unsafebitcast的好处是,它返回类型化的结果。 使用类型化的结果,我们可以调用其属性名称和方法,也可以对其进行修改。

CATransaction.flush()

为了在视图缓冲区(屏幕)内部的框架上反映出任何UIElement布局更新,应使用CATransaction.flush()命令。

定制调试文件

由于LLDB可使用python编写脚本,因此您可以完全访问所有LLDB API。 开发人员可以创建自己的python文件,以根据需要自定义调试命令。 我们可以将该文件导入Xcode的控制台,然后开始键入自定义调试命令。

下载Apple的.py文件以供参考-https://developer.apple.com/sample-code/wwdc/2018/UseScriptsToAddCustomCustomCommandsToLLDB.zip

为了解决与布局或约束相关的任何问题,必须具有内存地址。 如果我们有任何UI元素的出口,那么我们可以通过调试描述轻松访问其内存地址。 在其他情况下,当我们没有插座时,可以通过以下方式访问内存地址。

  1. 通过视图递归描述,找到所有子视图的内存地址。
  2. 要打印调试描述并获取类型结果,请使用unsafeBitcast命令。

3.修改引起问题的UI元素的框架或大小。

4.为了在屏幕上反映这些更改,请通过键入命令表达式CATransaction.flush()通知调试器。

5.我们还可以在调试模式下更新内容,而无需一次又一次地编译代码。

6.通过在Xcode可视调试模式下访问约束的内存地址来更新约束。 然后通过expresion命令对其进行更新,最后调用表达式CATransaction.flush()命令以更新视图缓冲区中的帧。

对于复杂的项目,上述技术可以节省大量时间,并且对于难以重现的错误至关重要。

参考-https://developer.apple.com/videos/play/wwdc2018/412/