数据冒险 犯了错误 有时,我们会对不正确的事情做出假设。 这种说法似乎很明显,这是人的天性。 编写软件时,最好质疑您的假设是否正确。 很多时候,您会发现,仔细检查后发现事情与最初出现的情况并不完全相同。 🤔挑战你的假设 使用Xcode时,可以质疑我们的假设的一种方法是使用调试器。 调试器是一种工具,可让您使用细粒度的控件检查和操纵代码执行。 通过调试,您可以更好地了解您的代码在做什么。 这篇文章旨在简要概述Xcode中的调试以及一些可以帮助您的工具。 我打算就调试主题撰写一系列文章,这是第一篇。 调试的主题非常广泛,本文并不旨在提供完整的概述。 如果您觉得缺少什么,请继续关注本系列的更多帖子。 另外,请随时给我评论。 崩溃的土地之旅 这听起来可能有些奇怪,但是可以将代码崩溃视为旅途。 当您点击运行时,它开始运行并编译。 在此过程中,它继续使用方法并具有许多特性。 最终,它成功地找到了坠机土地。 当您开始调试时,您只有一小幅图片。 您有起点和终点,但要知道如何到达那里,就需要追溯旅程的步骤。 为了帮助您完成旅程,Apple向您赠送了一个有价值的物品LLDB调试器。 控制台和LLDB LLDB是Apple提供的软件调试器,它是Xcode中的标准调试工具。 LLDB是一个功能强大的工具,可将应用程序分解为各个组件。 它的用途不仅限于Xcode IDE,即使您无法访问其源代码,也可以使用LLDB分解并检查应用程序。 鉴于功能和用途的广泛性,本文的含义更像是粗略的概述,我不想通过详细研究这些内容而超出范围。 调用堆栈 使用LLDB,我们能够检查调用堆栈以及堆栈中的特定堆栈帧 。 调用堆栈是由应用程序创建的数据结构,用于跟踪其所有行为。 数据结构 堆栈数据结构只是一个队列,在队列中添加的最后一个元素是从中返回的第一个元素(LIFO队列-先进先出)。这与物理世界中的情况大同小异:一堆砖的顶部(如果您从底部取出,即添加的第一块砖,它们都将崩溃。) 堆叠框架 在调用堆栈中有称为堆栈帧的结构。 堆栈框架包含有关为其创建执行的信息。 在调试时,许多此类信息可能会变得有用。 数据范围从局部变量到完成时返回的内存地址。 苹果文档: 程序运行时,它将有关正在执行的操作的信息存储在称为调用栈的数据结构中。 每次调用方法时,程序都会在调用堆栈的顶部推入一个新的堆栈框架 ,其中包含以下内容:传递给该方法的参数(如果有),该方法的局部变量(如果有的话)以及指向该地址的地址。方法调用完成后返回。 断点 如果您不熟悉代码断点,请允许我给您简要介绍一下。 在Xcode中,断点使您可以在特定点中断代码的执行。 这很有用,因为它使您可以在给定的时刻详细检查应用程序的状态。 断点类型 象征绕道 在进一步进入断点之前,让我们先谈谈符号化。 符号化是将内存地址映射到更简单的函数名称。 返回我们以前计划的编程 处理断点时,重要的是要了解有不止一种类型。 让我们简要地谈谈一些重要的类型: […]
框架变量varaibleName 打印此变量的类型 2.表达式variableBool = false 添加一个断点,为此断点添加动作,然后它将variableBool更改为false 3.为所有调用此函数的位置添加断点:符号断点 4. po $ arg1 — —打印汇编代码 5.断点集—一次正确—名称“-[UILabel setText:]”:断点仅有效一次 6.通过将断点拖动到下一行来跳过该行,或者添加断点并添加操作:线程跳转—加1 7.通过右键单击变量名称并单击监视变量名称来监视变量 8.expression -l objc -O — — [[‘self.view’XXXX]修复某些类型没有成员的错误 9.命令别名poc表达式-l objc -O — — 别名使命令更简单 视频参考:https://developer.apple.com/videos/play/wwdc2018/412/
情境 假设您作为iOS开发人员正在开发应用程序的UI组件。 您的PM来了,轻拍您的肩膀以请求更改特定按钮上的颜色。 通常在这种情况下,开发人员会感到不满意,因为这意味着他必须转到代码库来确定与按钮相关的代码的范围,更改其color属性,然后重建并运行该应用程序以验证颜色是否成功更改。 如果这样的请求不是那么频繁,那不是什么大麻烦。 但是,项目经理或设计师可能仍然有问题,请您反复尝试按钮上的不同颜色,直到他们对视觉效果满意为止。 我们是否必须修复代码并重新运行以检查一遍又一遍的agian? 有没有更有效的方法来实现这一目标? 答案是肯定的。 我们甚至不需要更改一行代码就可以做到。 魔术是LLDB表达式。 演示版 让我们有一个在iPhone模拟器上运行的非常简单的应用程序-只需单击视图上的一个按钮即可。 要进入调试模式,我们可以单击工具栏上的暂停按钮: 然后,我们可以输入以下命令: po [[[UIApplication sharedApplication] keyWindow] recursiveDescription] 此命令将打印出与视图层次结构有关的所有信息,包括其在内存中的地址及其布局。 它告诉我们按钮为0x7fb0c5f15900。 因此,让我们从内存中获取它: expression — id $testView = (id)0x7fb0c5f15900 expression表示执行以下命令并打印出结果,此处我们尝试将按钮传递给testView。 现在可以修改按钮的颜色了。 在testView上使用expression命令,我们刚刚获取: //将按钮的颜色设置为红色 expression — (void)[$testView setBackgroundColor:[UIColor redColor]] 为了见证更改,我们需要通过键入以下命令来刷新UI: expression — (void)[CATransaction flush] 然后您将看到按钮按预期变为红色! 总览 本文仅显示了一种使用LLDB表达式处理UI的方法。 LLDB是一个功能强大的工具,使我们能够设置按钮的文本,边框,甚至导航到另一个页面。 对于expression命令,它实际上是p和po的别名; p与expression –相同,而po等于expression -O — expression。 有关更多用法,请尝试使用help expression进行查找。
让我们一起深入课堂 Shanu:嘿Aaina! 我在项目中经常使用类,但是我想知道它的实际工作原理,在哪里保存数据以及所有内容。 我:好的Shanu。 类是引用类型。 将引用类型分配给变量或常量,或将它们传递给函数时,不会复制引用类型。 而不是副本,而是使用对相同现有实例的引用。 引用类型实例在堆上分配,并且变量仅包含对内存中存储数据位置的引用。 因此,理想情况下,数据将存在于堆中,而引用指针将存储在堆栈中。 对于同一个实例可能有多个变量引用,这很普遍。 这些引用中的任何一个都可以用于操纵实例。 在复制类时,它将继续指向相同的引用指针,并将保留计数增加1。 沙奴:乌夫 ! 理论太多。 您能详细说明一下吗? Aaina:好的。 我们来看一个类片段,看看它是对象图。 指定的初始化程序必须始终委托。 便利的初始值设定项必须始终委托。 必需的初始化程序: 使用required关键字,我们可以指示每个子类都需要实现此初始化器。 必需的init(){} Shanu:很酷,很好的信息。 现在让我们讨论对象图。 我:好的。 如果在此对象图中看到引用指针是在堆栈上分配的,而值是在堆中分配的空间的。 Shanu:哇,棒极了! 有什么办法可以在xcode中查看对象的地址吗? 我:是的,肯定。 我们可以通过代码或使用lldb查找地址。
这是我如何解决一个奇怪需求的简短简历,并发现了lldb高级命令的隐藏功能。 在谈论iOS / Mac开发中的框架时,通常会考虑在静态或动态库中构建的一组源代码文件,最终会嵌入一些其他资源。 有很多众所周知的工具(例如CocoaPods和Carthage)可以为您完成这些任务,因此这里没有什么新鲜事物。 最近,我们面临着一种可能被视为相反的方式:在我们的项目中有一个框架,我们有在另一个位置(项目之外)生成该框架的源文件,我们希望能够使用调试器逐步进入框架类。 那到底是为什么呢? 为了优化项目的构建时间,我们有一个特殊的流程,在该流程中,我们可以构建框架并将其提交到我们的仓库中,而不是源文件中。 尽管这有一些缺点可能值得在另一篇文章中讨论,但我们对结果感到非常满意,因为我们将构建时间减少了三分之一以上,从大约7分钟减少到大约2分钟。 此外,将我们的应用程序划分为多个框架迫使我们重新思考整个架构,从而清楚地将我们的依赖项隔离开来,这是一个不错的奖励! 我们构建的框架附带了DWARF调试信息,这些信息可以使用dwarfdump进行解析并转换为人类可读的内容。 该工具的使用真的非常有趣,并且可以提取的内容令人赞叹。 DWARF调试信息按节进行构造,每个编译单元一个节,并且通过调用dwarfdump –debug-info您将获得存储在.debug_info节中的所有信息。 其中包括符号之间的匹配以及源代码文件中的精确位置,如下面的屏幕快照所示。 请注意,源文件的路径是绝对路径,这意味着该文件甚至可能不存在(请考虑在另一台计算机上构建框架的情况)。 对我们来说幸运的是,我们正在不同的机器上构建框架,但是总是从同一路径/ tmp / SubitoFrameworksBuilder构建框架,由于这个假设,这只是重新映射路径的问题。 能做到吗? 是的,事实证明,使用lldb的以下命令确实很容易: settings set target.source-map ” ” 每次启动调试器时都要编写代码,这很繁琐,但是好消息是,每次启动应用程序时,都有一种方法可以通过将上述行添加到~/.lldbinit-Xcode来自动执行。 能够按项目而不是按用户执行此操作会很好,但是不幸的是,有一个开放的雷达坐在那里一段时间。 ~/.lldbinit-Xcode每一行都~/.lldbinit-Xcode执行,从而允许您定义别名和自定义命令。 值得检查一下Facebook已发布的功能非常强大的功能。 回顾一下:通过一个lldb命令,我们现在可以将调试器与我们的预构建框架一起使用,在文件系统中的某个位置具有源代码文件。 😎
– (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"LibraryListingCell"; InSeasonCell *cell = (InSeasonCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { [[NSBundle mainBundle] loadNibNamed:@"InSeasonCellView" owner:self options:nil]; cell = [_cell autorelease]; _cell = nil; } if(_dataController!=NULL){ Product *productAtIndex = [_dataController objectInListAtIndex:indexPath.row]; // Configure the cell… if (productAtIndex.name != nil && productAtIndex.week != nil && […]
我已经在下面声明了一个全局数组/字典。 @interface ViewController () { NSDictionary *dictionary; NSArray *array; } 我正在进行API调用并将值存储在这些数组和字典中。 我想在运行期间打印LLDB中的数组/字典的内容。 当我尝试打印这些全局variables,它给了我下面的错误。 (lldb) po assigneeArr error: warning: Stopped in a context claiming to capture an Objective-C object pointer, but 'self' isn't available; pretending we are in a generic context error: use of undeclared identifier 'assigneeArr' error: 1 errors parsing expression 那么我怎么能debugging这个全局声明的variables?
我有一个与Cocos2D和一些UIKit混合在一个应用程序。我最近一直在实施一些新的function,所以也许内存pipe理是错误的或过度发布的东西,但每当我退出应用程序通过单击主页button,控制台崩溃只有消息(lldb)。 即使我打开NSZombiesEnabled没有什么不同。 我的应用程序指向的唯一一条线是这样的: libGPUSupportMercury.dylib`gpus_ReturnNotPermittedKillClient: 0x38101094: trap 0x38101096: nop 我不知道这是什么意思,但我很想得到这个修复。 有谁知道为什么会发生这种情况? 提前致谢! 编辑 :我认为这是因为我调用Cocos2D的CCDirector后,我做unscheduleAllSelectors和stopAnimation的原因。 如果我不叫这两个,那么就不会有任何的崩溃。
每当我把一个断点,并尝试访问任何variables,我得到这个错误。 如果我试图在控制台中打印一个variables,我得到这个错误: (lldb) p someVar error: Error in auto-import: failed to get module 'Jogabo' from AST context: /Users/pg/Jogabo/app-v2/ios- app/Pods/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginButton.h:21:9: note: while building module 'FBSDKCoreKit' imported from /Users/pg/Jogabo/app-v2/ios- app/Pods/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginKit/FBSDKLoginButton.h:21: #import <FBSDKCoreKit/FBSDKButton.h> ^ <module-includes>:1:9: note: in file included from <module-includes>:1: #import "/Users/pg/Jogabo/app-v2/ios-app/Pods/Target Support Files/FBSDKCoreKit/FBSDKCoreKit-umbrella.h" ^ /Users/pg/Jogabo/app-v2/ios-app/Pods/Target Support Files/FBSDKCoreKit/FBSDKCoreKit-umbrella.h:6:9: note: in file included from /Users/pg/Jogabo/app-v2/ios-app/Pods/Target Support Files/FBSDKCoreKit/FBSDKCoreKit-umbrella.h:6: #import […]
(lldb) po [NSString stringWithFormat:@"%.1f", 0.01] (id) $21 = 0x003a2560 19991592471028323832250853378750414848.0 (lldb) po [NSString stringWithFormat:@"%.1f", 0.1] (id) $22 = 0x0de92240 -0.0 有没有人了解这里的行为? 我在设备上运行。