高级调试

调试区域是Xcode中iOS开发人员的一项有用功能。 一旦我们开始精通iOS的开发,并尝试摆脱熟悉和喜爱的打印方法,并找到更快,更方便的方法来了解特定时期的系统状态,我们便开始研究Debug Area。

最有可能的是,在“调试”面板中,您的眼睛会落在您确切了解那里发生的事情之前。 当应用程序第一次崩溃时,下方的菜单会自动打开,它最初可以帮助您理解问题(记住旧的“致命错误:索引超出范围”),基本上,一开始您将不了解Xcode想要从我们这里获取什么并通过Google进行搜索错误,但是在成长过程中,越来越多的信息将变得清晰。

从一开始,程序员就试图优化他的工作。 为此,我们努力了解程序在什么时候进入了错误状态。 在这里,根据程序员的发展定位,方法可能会有所不同。 首先,如何通过“ print()”方法正确完成调试,然后放置断点并调用“ po”方法,然后熟悉“调试变量输入”(Xcode中控制台旁边的区域),然后了解如何在停止时编译代码断点方法-“表达”(至少那是我的发展)。

让我们尝试不同的方法,这些方法将有助于我们理解和更改应用程序的状态。 不会考虑最简单的参数,例如“ print()”和“ po”,我想您已经了解它们的本质并知道如何应用它。

让我们用一个屏幕创建一个简单的应用程序,其中只有一种类型的单元格(TableViewcell),其中包含两个元素:UIImageView和UILabel。 在单元格中,我们将写入其序列号,并将image1或image2放入图片中。

tableViewCellForRowAtIndexPath方法将为我们创建单元格,放入数据并返回:

图片

此方法将生成下表:

图片

断点


让我们停止程序并向Label添加一些文本。

1.设置断点:

图片

2.分配文本后,程序立即在第55行停止执行。 由于我们位于该单元格视野中的一条线上,因此我们可以与该单元格进行交互。

3.我们在控制台中编写命令以更改单元格的文本:

图片

4.我们删除断点并按下按钮“继续执行程序”。

5.在电话的屏幕上,我们看到一切都成功完成:

图片

expression执行表达式并在当前线程上返回一个值。

编辑断点


但是,如果我们需要在大量单元格中更改文本怎么办? 还是在程序执行过程中已经意识到需要更改?

我们可以优化此操作的执行速度并加快工作速度,在到达断点时将文本更改为单元格并继续执行程序,这将节省大量时间,并且使我们不必为每个单元格打印相同的内容。

为此,我们需要稍微修改断点,在此添加一个附加代码,这将在单元格的可视区域更改其文本并继续执行该程序。

  1. 创建断点。
  2. 左键单击断点箭头。
  3. 单击编辑断点。
  4. 条件-断点可以工作的条件,现在我们不需要它。
  5. 忽略-在断点起作用之前跳过断点多少次(也不是)。
  6. 但是Action是我们需要的,选择Debugger Command的类型。
  7. 我们编写需要执行的表达式:
  8. 表达式cell.desriptionTextOutlet.text =“ \(indexPath.item)任务完成”。
  9. 打勾-成功完成命令后继续执行。

图片

9.我们正在努力。

图片

这是成功的,事实证明,在表的形成过程中可以更改每个单元格的文本,而我们不必为每个单元牺牲时间和规定操作。

断点功能


有时,我们的应用程序中发生某些无法解释的事情,文本没有发生变化或发生了不必要的变化,在这种情况下,断点似乎无处可放。 但这不是完全正确的,如果您了解Obj-C,并且知道编译器要跟踪的方法,则可以在其上放置Breakpoint,然后在下次调用该方法时,应用程序将在执行汇编代码的过程中停止。

1.在Breakpoint导航器中,选择Symbolic Breakpoint。

图片

2.我们要跟踪在单元格中设置文本的方法,请写-[UILabel setText:]。

图片

3.零参数不存在,并且计数从第一个开始。 捕获的第一个方法不是我们需要的(我们将当前时间设置到状态栏),而第二个方法只是我们的:

4.在“ $ arg1”下存储对象的描述。

5.在“ $ arg2”下存储选择器功能。

6.在“ $ arg3”下存储通过该方法获得的文本。

好的,这似乎很清楚。 但是有时在某些情况下,状态栏上的文本安装不限于此,您需要在特定控制器中跟踪该方法的执行情况,应该怎么做? 您可以启用断点,类似于我们先前安装的断点,但是可以在代码中设置其位置。 这是什么意思? 我们肯定知道,当我们在单元格中安装文本时,视图就会出现,所以最重要的是将其放在viewDidLoad中或在创建单元格之后。

要创建一个断点,我们将其设置在行上,然后在操作中编写以下代码:

breakpoint set --one-shot true --name "-[UILabel setText:]” 

breakpoint set —one-shot true -创建断点
—name是字符断点的名称
“-[UILabel setText:]”称为方法

这是发生了什么:

图片

跳过线



但是,如果我们怀疑某些代码会破坏整个程序,该怎么办? 在执行代码期间,可以避免执行如下特定的代码行:

  1. 我们将断点放在我们不想执行的行上。
  2. 当执行停止时,将其拖到我们要继续执行程序的行(这很有趣,但它并不总是有效,不拖拽的选项会降低)。

还有另一个可以优化跳行的选项-这是“编辑断点”中相应命令的规定。 团队冒险,因为这种跳跃的本质是使我们免于重建,但是如果您跳过对象的初始化并尝试与它联系,程序将崩溃。

图片

让我们停止程序初始化图片的过程,我们根本不会将图片分配给单元格,为此我们需要跳过五行代码并返回没有图片的单元格,为此我们跳过当前线程上以下五行代码的执行,然后继续执行程序:

图片

听起来不错,但我仍然想分配图片,让我们向断点添加分配方法:

图片

很好的组合,现在每个单元格中只有一种类型的图片。

观察点


调试器中的另一个方便功能是跟踪程序中的值,观察点。 监视点与KVO有点类似,我们设置一个断点来更改对象的状态,并且每次更改对象的状态时,程序执行过程都会停止,我们可以看到该值以及更改该值和更改对象的位置。 例如,我在一个单元格上放置了一个观察点,以了解在分页表并初始化新单元格时会发生什么。 事实证明命令列表非常大,因此我只想提一下它:执行位于单元格内部的布局视图并设置约束,动画,单元格的设置状态等等。

要将观察点设置为一个值,必须在要监视的属性范围内停止断点程序,在“调试变量”面板中选择该属性,然后选择监视“ <参数>”。

图片

为了使用变量删除观察点,您需要查看断点导航器,在那里,其余的断点将成为我们的观察点。

断点UI更改


有时我们需要更多地了解我们要排斥的物体。 最简单的选择是使用“ po”显示有关对象的信息,然后在其中查看对象在内存中的位置。 但是,碰巧我们没有直接链接到对象的对象;该对象未在API视图中表示,该视图位于库中或可能被库隐藏。 其中一种方法是使用“视图层次结构”,但这并不总是很方便,也不总是很难理解您已经找到了所需的视图。 您可以尝试使用以下命令:

 expression self.view.recursiveDescription() 

它在Obj-C中,但是由于语言的特殊性而在Swift中被删除,我们无法做到这一点,但是由于Debuger与Obj-C一起使用,理论上他可以提供此命令,并且他将从他那里了解您想要的内容。 要在控制台中执行Obj-C代码,必须输入以下命令:

 expression -l objc -O - - [`self.view` recursiveDescription] 

您在这里看到什么? 我看到一个相当不便的结构可能会随着时间的流逝而习惯,但我们最好不要这样做,而要使用typealias简化命令:

 command alias poc expression -l objc -O — 

现在我们的团队正在收缩和简化,但仍在继续工作:

 poc [`self.view` recursiveDescription] 

关闭Xcode后或在另一个项目中它会工作吗? las,不。 但是可以解决! 通过创建.lldbinit文件并在此处输入别名。 如果您不知道如何操作,请参阅以下项目说明:

1.创建一个.lldbinit文件(您可以将.gitignore作为原型,它属于同一类型的文本不可见文件)。

2.在此文件中准确编写以下命令:

  command alias poc expression -l objc -O - - 

3.通过地址“ MacintoshHD /用户/ <这里是您的用户名>”将文件放在文件夹中。

这样我们就对屏幕上显示的所有视图进行了描述。 让我们尝试看看如何使用内存中的对象地址。 对于Swift,有一个缺点,您必须始终将内存中对象的类型强制转换为特定值:

 po unsafeBitCast(0x105508410, to: UIImageView.self) 

现在我们可以看到图片在单元格中的位置,我们将其移动,使其位于单元格的中心,并在20 px的一侧有一个凹口。

图片

有时,更改不会立即引起注意,但是有必要从调试中删除应用程序以注意更改。

但是,如果我们想在每个单元中看到类似的内容,则需要加快命令的执行速度,我们可以使用Python编写几个对我们有用的脚本(您可以在此处查看如何添加脚本, 网址为www.raywenderlich.com/612-custom-lldb-commands-in-实践 ),如果您知道如何处理Python并想为lldb编写Python,那么您会派上用场。

我决定为UIView类编写一个扩展,该扩展将简单地朝正确的方向移动视图,在我看来,将新脚本连接到LLDB的问题会更少,并且对于任何iOS程序员来说都不难(否则您需要为LLDB学习Python)。

我没有在内存中寻找对象的位置,而是将其带到所需的类,这样我以后要占用一帧,也将花费太多时间。 这个问题是通过在UIView扩展中编写一个函数来解决的:

图片

不幸的是,它不适用于单元格,这很可能是由于以下事实:在执行冲洗命令时,并非所有单元格位置都已计算出来,并且它没有出现在屏幕上(我们还没有返回tableViewCell)。 与其他静态元素一起使用,效果很好。

知道视图在层次结构中的位置,我们可以访问它并更改其位置。

现在相反的情况是,当我们可以访问ViewHierarchy并希望从那里获取视图数据时。 在Xcode中,可以在程序执行期间查看视图层次结构,还可以查看颜色,布局,类型以及与其他对象(包括该对象)的绑定。 让我们尝试访问UIImageView的约束。

要获取约束数据:

1.单击“调试视图层次结构”。
2.在出现的屏幕底部的面板中启用“裁剪的内容”。
3.在同一面板中启用约束。
4.选择约束。
5.在菜单中,单击编辑->复制(命令+ C)。
6.复制这种绑定:((NSLayoutConstraint *)0x2838a39d0)。
7.现在,就像我们通过代码对其进行更改一样,您也可以在lldb中对其进行更改:
expression [((NSLayoutConstraint *)0x2838a39d0) setConstant: 60]
8.单击继续按钮后,该项目将更新其在屏幕上的位置。

您可以使用相同的方式更改颜色,文本等:

 expression [(UILabel *)0x102d0a260] setTextColor: UIColor.whiteColor] 

演示项目原来太简单了(ViewController中有60行代码),我编写的大多数代码都在本文中介绍了,因此再现测试项目不会有困难。

PS:如果您有任何疑问或意见,请写。 看看WWDC和Pro辩论。

我还建议您熟悉这些材料:

受到Advanced Debugger WWDC 18 Session的启发
调试器命令
将Python脚本添加到LLDB Xcode

Source: https://habr.com/ru/post/zh-CN460779/


All Articles