- 原文地址:Xcode and LLDB Advanced Debugging Tutorial: Part 3
- 原文做者:Fady Derias
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:kirinzer
- 校对者:swants, iWeslie
在这三部分教程的第一部分和第二部分中,咱们已经介绍了如何利用 Xcode 断点来控制一个存在的属性值,而且经过表达式语句注入新的代码行。咱们还探索了观察点这种特殊类型的断点。前端
我开发了一个特地带有几个错误的演示项目,详细说明了如何使用不一样类型的断点配合 LLDB 来修复项目/应用程序中的错误。android
若是你尚未看过本教程的 第一部分 和 第二部分,最好先看过它们再继续阅读本文。ios
最后,本教程的指导原则是:git
第一次运行应用程序后,你没必要中止编译器或从新运行应用程序,你将在运行时修复这些错误。github
到目前为止咱们还要作什么?express
- 导航栏左侧指示用户加载次数的标签没有更新。
这里有一些步骤能够复现这最后一个须要处理的错误:json
✦ 滚动到表视图的顶部,而后下拉刷新。后端
✦ 滚动到表视图的底部去加载更多文章。[7 次 😉]xcode
✦ 在每次成功获取到新的文章以后,左侧标签并无被更新。bash
须要指出的是整型属性 pageNumber
回答了这个问题,用户已经加载了文章多少次?(换句话说,导航栏左侧的标签应该被 pageNumber
属性的值更新)。咱们能够确信的是,在以前的修复中 pageNumber
属性的值已经能够更新了。所以如今的问题在于没有将它的值设置给导航栏左侧的标签。
在这种状况下,符号断点会介入。想象一下,符号断点就像调试器在玩寻宝,而你会提供一些寻宝的线索。对你来讲,这会发生在更新导航栏左侧标签的代码片断中。
让我告诉你接下来怎么作。
展开 Breakpoint navigator,接着点击左下角 + 按钮,选择 Symbolic Breakpoint。
在 Symbol 栏添加以下符号
[UILabel setText:]
复制代码
不要 勾选 “Automatically continue after evaluating actions” 选项框。
咱们所作的只是告诉调试器,当任何一个 UILabel 的 setText 方法被调用的时候,它就会暂停。注意这里在建立了一个符号断点以后,一个子断点会被添加。
这是来自调试器的反馈,它可以解析这个建立的符号断点到 UIKitCore
框架的特定位置。在其余状况下,调试器也许会解析到多个位置。
如今一切就绪,下拉以刷新表视图的文章。当你释放以后,调试器就会暂停,接着你会看到以下图的东西:
在这时你会看到一些 UIKitCore 框架的汇编代码,在左侧的是致使调试器暂停的堆栈信息。下一步咱们要作的是,检查在调试器暂停的位置传入 Objective-C 消息的参数。在 lldb 控制台输入下面的命令:
po $arg1
复制代码
这会指出持有第一个参数的寄存器。咱们能清楚的看到接受这个 Objective-C 消息的是一个 UILabel 实例。这个 UILabel 实例有一个文本值指向一个文章的标签。这不是咱们所感兴趣的,不过让咱们继续寄存器检查。
在 lldb 控制台,输入以下指令:
po $arg2
复制代码
$arg2 始终指向 Objective-C 消息的选择器。在某些状况下,lldb 并不彻底的清楚参数的类型,所以咱们须要作一些类型转换的工做。
在 lldb 控制台,输入以下指令:
po (SEL)$arg2
复制代码
如今咱们很清楚的看到了当前 Objective-C 消息的选择器。
在 lldb 控制台,输入以下指令:
po $arg3
复制代码
$arg3 始终指向传入方法的第一个参数。在咱们的情形下,传入 setText 方法的参数一个字符串。
继续执行程序。调试器会再次暂停。重复前面的步骤,最终,你发现这个 Objective-C 消息属于在表视图里的另外一个文章标签。直到咱们找到咱们感兴趣的那个 UILabel 实例前,一遍又一遍的作这个事情确实很无趣。确定有更好的方式。
你可以作的一件事就是为符号断点设置条件,以便在成功或知足条件时暂停调试器。它可以检查布尔值或者等待条件达成诸如此类。
然而,咱们采用一种不一样的方法。
将咱们建立的符号断点设置为不可用。
讲道理,导航栏左侧的标签指示了用户加载文章的次数,它会在 HTTP GET 请求成功完成以后被更新。找到有 pragma mark Networking
的部分。在 loadPosts
成功完成的回调里放置一个断点。这个断点应该放在以下的位置:
Objective-C
[self.tableView reloadData];
复制代码
Swift
self.tableView.reloadData()
复制代码
这会确保符号断点只有在表视图从新加载数据以后才会被触发,全部相等的标签都已经被更新。
不要 勾选 “Automatically continue after evaluating actions” 选项框。添加以下的调试器命令动做:
breakpoint set --one-shot true -name '-[UILabel setText:]'
复制代码
🤨🧐🤔
让咱们拆解这个命令:
breakpoint set --one-shot true 会建立一个 “one-short” 断点。one-shot 断点是一种建立以后,首次触发就会自动删除的断点。
-name ‘- [UILabel setText:]’
给建立的 one-shot 断点设置了一个符号名。这和你上一节所作的很是类似。
让我总结一下这一部分。你所作的有:
在发起 GET 请求成功完成的回调里添加断点(A)。
添加调试器命令动做去 建立 符号断点(B)和上一节建立的很类似。这个符号是 UILabel
setText
方法。
将你建立的符号断点(B)设置为一个 one-shot 断点。one-shot 断点在触发后会被自动删除,这意味着符号断点只会暂停调试器一次。
断点(A)被放置在表视图加载完成以后,所以建立的符号断点(B)不会因任何和表视图相关联的标签而暂停调试器。
如今下拉表视图去刷新。咱们会获得以下内容:
因为设置了 one-shot 断点调试器停在了断点(A)的位置。
继续执行程序。
你会返回到 UIKitCore 框架的汇编代码。
让咱们检查一下符号断点参数的 Objective-C 消息。
在 lldb 控制台,输入以下的指令:
po $arg1
复制代码
哇哦,看起来你找到了宝藏! 🥇🏆🎉
是时候把咱们的目光转移到堆栈跟踪信息了。走到点 1 的位置。
它会引导你到这块更新 pageNumberLabel
文本的代码。这块代码很明显为文本始终设置了整型值为 0
而不是 pageNumber
属性的格式字符串。让咱们在实际修改代码前先测试一下。
你如今已是行家了 🧢
在已标记的代码分隔线下添加一个断点。添加以下的调试器命令动做:
Objective-C
expression self.pageNumberLabel.text = [NSString stringWithFormat:@"Page %tu", self.pageNumber]
复制代码
Swift
expression pageNumberLabel.text = String(format: "Page %tu", pageNumber)
复制代码
移除或者停用断点(A),相应地,断点(B)也会被停用。
如今下拉刷新和加载更多文章。左侧导航栏标签将会被更新。 🎉
任务完成! 💪 💪
如今你能够中止编译器而且在代码中去修复咱们讨论的这些问题。
在这个教程里,你学会了
如何使用断点配合调试器动做表达式去控制存在的属性值。
如何使用断点配合调试器动做表达式注入代码。
如何为某个属性设置观察点监视属性值的变化。
如何基于定义的符号使用符号断点暂停调试器。
如何使用 one-shot 断点。
如何使用 one-shot 断点配合符号断点。
调试愉快! 😊
为了本教程的方便,我使用了下面的第三方工具。
若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。