ios 调试技巧 (1) - Diagnosing and resolving bugs in your running app

683 阅读12分钟

解决应用程序的问题

检查你的应用程序以找到错误、定位崩溃、识别过多的系统资源使用情况、内存问题和视图布局问题。

概述

单元测试确定您的代码是否提供了满足您期望的结果,但当它不满足时不解释原因。要使用调试器诊断错误,请重现错误,然后通过在应用程序使用断点运行时检查代码中关键点处的变量来缩小其根本原因。按照相同的过程来诊断和解决代码中的错误、内存泄漏和Auto Layout 问题。

暂停应用程序来检查变量并且找出错误

修复错误的第一步是了解导致它的原因。要缩小错误的原因,请制定一组可靠地重现错误的步骤,然后按照以下步骤操作:

  1. 确定您认为错误发生在源代码中的位置。

  2. 在源代码中您怀疑发生错误的位置之前使用断点暂停您的应用程序。

  3. 查看您的变量并确认它们具有您期望的值。如果没有,请从第 1 步重新开始。

  4. 单步执行您的代码,并观察变量的变化。请注意您的变量在哪里具有意外的值。

  5. 分析您的代码以确定修复程序。

在确定错误的潜在修复后,通过更改代码并重新测试以重现它来确认诊断。如果您的更改解决了问题,那么您已经解决了错误。如果您的更改没有解决它,请重新考虑错误可能发生的位置,并重复这些步骤以隔离和修复它。

For more information, see Setting Breakpoints to Pause Your Running App and Stepping Through Code and Inspecting Variables to Isolate Bugs.

定位崩溃、异常和运行时问题

当您的应用程序遇到崩溃、异常或运行时问题时,可能很难准确找出导致问题的代码,因为崩溃的堆栈跟踪并不总是指向导致崩溃的代码行。确定您遇到的问题类型,以便设置正确的断点类型:

  1. 在 main 处停止或 highlights 在 AppDelegate 的崩溃通常是 Objective-C 异常。

  2. 由运行时问题导致的崩溃也会在 main 或 highlights AppDelegate 处停止,并且可能会显示类似于:“Thread 8: EXC_BAD_INSTRUCTION (code=...)”的消息。

3.  在未捕获或未处理的 Swift 错误处停止的崩溃会显示致命错误消息并指示 Swift 错误。

Add a breakpoint for the type of problem causing the crash to locate where in your code the crash occurs. For more information, see Setting Breakpoints to Pause Your Running App and Identifying the Cause of Common Crashes.


注意点;

当您的 Swift 代码使用来自 iOS、tvOS、watchOS 或 macOS SDK 的代码时,它可能会收到一个 Objective-C 异常,因为这些 SDK 使用的是 Objective-C。

在不暂停的情况下检查变量和执行顺序

开发代码时,记录操作和变量值会很有帮助,这样您就可以了解代码的运行方式以及变量在应用程序中不同点的值。当您开发并发代码(跨多个队列或线程同时执行的代码)时尤其如此,因为错误可能是间歇性的并且难以重现。通常,您会在正常执行中重现错误,但在单步执行调试器时不会重现。出现这个问题是因为正常执行和调试的时间不同。调试器提供了检查变量的工具,而不会暂停和干扰并发代码的时序。

开发人员通常添加 print 或 NSLog 语句来查看变量值。虽然这种技术有效,但它会添加在您完成开发后无用的额外代码,并为您的应用程序留下一个乱七八糟的控制台,从而使诊断后续错误变得更加困难。相反,使用断点操作来了解应用程序中的事件何时发生,并在不暂停的情况下检查变量值。

要确定您的代码执行时对计时的影响是否最小,请使用断点操作播放声音并继续执行。如果调试器在您运行应用程序时到达断点,它将播放选定的声音,确认您的代码的执行。

要在不暂停的情况下将变量值记录到控制台,请使用 po 或 v 添加带有调试器命令操作的断点,以将变量的值打印到控制台。为断点选择“评估操作后自动继续”选项以防止暂停。

diagnosing-and-resolving-bugs-in-your-running-app-1@2x.png

要将自定义文本记录到控制台,而不仅仅是一个变量值,请添加一个带有 Log Message 操作的断点。指定您的自定义文本,并包含表达式、断点名称或断点命中计数以提供更多信息。


注意点;

由于 po 动态编译代码以评估表达式,因此评估变量并将其记录到控制台需要更多时间。为了减少时间问题,请使用 v 来记录变量值。

使用其他断点操作来执行 AppleScript 或 shell 脚本,或捕获 GPU 帧。

For more information, see Setting Breakpoints to Pause Your Running App and Stepping Through Codeand Inspecting Variables to Isolate Bugs.

识别潜在的系统资源过度使用情况

开发和测试中一个常见的容易被忽视的问题是 CPU、内存、磁盘访问和网络流量的过度使用。 Xcode 的调试器在 Debug navigator 中提供仪表以帮助调查潜在问题。在测试应用时监控仪表以发现异常使用情况。单击仪表以查看更详细的视图。

diagnosing-and-resolving-bugs-in-your-running-app-2@2x.png

CPU 量表显示应用程序需要多少 CPU 来处理一段时间内的指令。当您的应用程序在绘制用户界面、处理从网络检索的数据或执行计算时,CPU 使用率增加是正常的——在某些情况下,会在短时间内达到相当高的数字。当这些任务完成并且您的应用程序处于空闲状态并等待用户执行操作时,CPU 使用率应该为零或非常低。如果出现以下情况,请进行一些额外的分析:

  • 当应用程序似乎处于空闲状态时,CPU 使用率始终保持在零以上。

  • CPU 使用率在很短的时间内变得非常高(超过 100%)。

  • CPU 使用率非常高,您会在用户界面中看到故障。

For more information, see Improving your app’s performance.

内存量表显示您的应用在一段时间内使用了多少内存。当您第一次启动您的应用程序时,它从一个相当小的数字(小于 10 MB)开始,然后随着用户在您的用户界面中导航而增加。如果您从网络获取、处理和存储数据,或者执行复杂的计算,它也可能会增加,然后在处理完成时减少。在浏览应用程序时观察仪表,并注意内存使用量何时上升和何时下降。当您 present modal views 或将视图添加到导航控制器时,内存使用量会增加,而当您关闭或导航离开这些视图时,内存使用量会减少。如果您的使用量继续增加并且从未减少,请检查您是否存在内存泄漏或废弃内存。有关更多信息,请参阅减少应用程序的内存使用。有关解决内存泄漏的更多信息,请参阅下面的可视化和诊断内存使用量增加。

磁盘 I/O 仪表显示您的应用程序随时间从磁盘读取和写入的数据量。如果您:

  • 存储用户在您的应用中生成的数据

  • 在用户首选项中存储数据

  • 从网络中获取数据并存储

  • 从您的应用程序包或应用程序目录中读取数据

从磁盘存储和读取数据通常比从内存中存储和读取数据消耗更多的能量,并且会增加用户设备的磨损。了解磁盘使用是否异常需要了解您正在存储和读取的数据的大小,以便您可以将其与您在仪表中观察到的数据进行比较。例如,如果您正在下载和存储一个 5 MB 的图形文件显示在view上,并且它写入了超过 50 MB 的数据,请调查remote image 是否经常更改,或者您是否需要配置网络以防止重新下载相同的图像。如果您从磁盘读取的数据比您预期的要多,请调查内存缓存解决方案是否有帮助,或者您是否从应用程序或视图生命周期中的错误点开始读取数据,并且过于频繁地读取数据。有关详细信息,请参阅减少磁盘写入。

Network I/O  仪表显示您的应用程序随时间从网络读取和写入网络的数据量。如果您的应用仅使用本地资源,则您的应用可能无法从网络读取或写入任何数据。通过网络传输数据会消耗能源并缩短设备的电池寿命,因此请尽可能减少数据传输。当您的应用程序从网络发送或接收数据时,请观察网络 I/O 仪表。例如,如果您为下载的图像实施缓存系统并且访问它们时网络使用量增加,请确认您的缓存设置在应用程序和服务器上是正确的。如果您要上传用户生成的内容,并且在网络状况不佳的情况下频繁上传失败导致网络使用率高,请使用断点续传,而不是上传整个文件。

###可视化和诊断不断增加的内存使用情况

通过内存图诊断内存泄漏和废弃内存的原因。内存泄漏的明显症状是内存使用量随着时间的推移持续增加,即使应用程序中的条件表明内存使用量应该减少。内存泄漏可能发生在保留周期中,即对象之间保持强引用,但应用程序不再引用它们。这些对象保留在内存中,应用程序无法删除它们。当您创建对象并且您的代码仍然引用它们,但您的应用程序不再需要它们或使用它们时,就会发生废弃内存。

diagnosing-and-resolving-bugs-in-your-running-app-3@2x.png

要在调试器中查看内存图,请在断点处暂停您的应用,然后单击调试栏中的“调试内存图”按钮。或者,在应用程序运行时单击“调试内存图”按钮以暂停应用程序并显示内存图。

内存图视图将调试导航器中的堆栈跟踪替换为按库组织的类型列表,每个类型都有一个称为节点的实例列表。选择一个节点以查看其内存图。

节点的内存图显示对该节点的所有内存引用,并highlights强引用。按住 Control 键单击图表中的任何节点以执行更多操作,例如访问 Quick Look 或将描述打印到控制台。选择关注节点以显示所选节点的图表。单击引用可查看其详细信息,包括变量名称、引用类型以及内存中的源对象和目标对象。

diagnosing-and-resolving-bugs-in-your-running-app-4@2x.png

请按照以下步骤解决 a memory leak for a retain cycle:

  • 浏览应用程序时观察内存量表。

  • 请注意,当您的应用程序实例化一个对象时内存使用量会增加,但当您希望系统释放该对象时内存使用量不会减少。

  • 检查内存图以查看是否存在意外数量的对象实例或对它的不适当的强引用。

  • 如果对象存在强引用,按住 Control 键单击具有强引用的节点并选择“Focus on Node”以查看其图表。如果节点还具有来自对象的强引用,则您已诊断出保留周期。

通过将关系的一侧更改为对另一个对象的引用使用弱声明来解决保留循环,或者通过删除对另一个对象的任何依赖关系来完全删除引用。重新测试以确认更改解决了问题。

###检查并解决view Layout 相关问题

使用视图调试器来调试为什么用户界面中的view的位置不对或大小不对。在应用呈现视图后(例如,在 viewDidAppear: 方法中)在应用中设置断点,然后在调试器在断点处暂停时单击调试栏中的 View Debugger 按钮。或者,只需在您的应用呈现视图后单击 View Debugger 按钮。

调试器在中心的画布上显示当前视图的 3D 渲染,并在调试导航器中显示视图层次结构。向任意方向拖动视图以查看当前视图堆栈的三维表示,并使用画布底部的控件来调整视图和它们之间的间距。

单击以在调试导航器中的视觉渲染或视图层次结构中选择一个视图,然后在对象检查器或大小检查器中检查详细信息。根据布局类型解决您的布局问题:

diagnosing-and-resolving-bugs-in-your-running-app-5@2x.png

在基于框架的布局中,视图调试器会显示您在大小检查器中指定的框架。如果大小不符合您的预期,请逐步检查代码以诊断帧计算问题。然后修复并重新测试。

在使用自动布局的视图中,尺寸检查器会显示约束。单击约束以在视图调试器中突出显示它。分析影响放错位置或尺寸错误的视图的约束以诊断问题。在您的代码或 Interface Builder 中调整您的约束,然后重新测试。

在使用 SwiftUI 构建的视图中,大小检查器显示 SwiftUI 如何解析视图的大小和位置。使用这些信息,分析和调整您的 SwiftUI 代码并重新测试。