在条件断点中访问类
in 技术 with 0 comment

在条件断点中访问类

in 技术 with 0 comment

前言

有时候我们需要在 Xcode 断点里面加入一个条件,能帮助我们减少无意义的断点次数,快速点位问题。那么我们一定遇到过下面的问题。

问题

首先,我们在断点里面填写需要的条件。当 dataObjectSubClassOfBaseDataObject 类型的时候,则断点。

//In breakpoint
[dataObject isKindOfClass:[SubClassOfBaseDataObject class]]

接下来运行我们的程序,控制台却给了我们一个大大的惊喜(报错)。它尽然说 class 是未知方法。

Stopped due to an error evaluating condition of breakpoint 11.1: "[dataObject isKindOfClass:[SubClassOfBaseDataObject class]]"
Couldn't parse conditional expression:
error: no known method '+class'; cast the message send to the method's return type
error: 1 errors parsing expression

最后,如果我们在断点之前,在 .m 文件中插入如下临时变量 subClassCheck,并在断点条件中引用这个变量。则不会有任何报错。具体原因,请看下面分解。

//In .m file
Class subClassCheck = [SubClassOfBaseDataObject class];
//In breakpoint
[dataObject isKindOfClass:subClassCheck]

原因

基于像 Cocoa 这样的大型框架,调试代码的一个复杂之处在于,在整个框架中,编译器发出或调试器去消耗每种类型和函数都是不切实际的。
因此编译器使用一些启发式方法来减少生成的调试信息量。它将仅为您实际使用的类型和定义的 函数/ObjC方法 发出类型信息(与在头文件中声明的相反)。还有另一个小小的微妙之处,即 lldb 会读取 ObjC 运行时之外的方法的类型信息,尽管这些信息并不完整,因为它是适用于运行时而不是调试器...因此,有时我们似乎对违反先前规则的 ObjC 方法有所了解。

另一个需要注意的重要事项是,调用返回大于指针(如 NSMakeRect 等)的函数的约定是这样的,如果调试器调用一个函数,认为它返回一个指针,它实际上返回一个更大的结构,该行为将导致程序中的堆栈损坏。
如果你很幸运,你会在你继续执行时立即崩溃,但如果你运气不好,它只会改变一些数据值并导致你花费数小时试图追逐一些实际上由调试器引起的有趣行为。因此调试器将拒绝调用无法确定其返回类型的函数。

无论如何,你得到的错误是因为调试器无法在对象上找到 '+ class' 方法的调试信息。这并不奇怪,因为 'class' 是 NSObject 上的一种方法而不是你的类。我不知道为什么我们在运行时找不到它,也许是因为它是一个类方法?这是个有意思的 bug。我们显然从运行时通过 isKindOfClass 确定了类型,否则我们的解决方法也会失败。

在这种情况下,由于您实际上知道类方法的返回类型,因此可以通过在断点表达式中显式地将其转换为具体类型来解决调试器知识不足的问题。在调试器的表达式解析器中转换函数返回有两个目的,一个是常规C语言函数,另一个是告诉调试器函数的返回类型,否则它无法弄清楚。一种仅适用于返回类型的速记原型。

就像下面这样:

[dataObject isKindOfClass: (Class) [SubClassOfBaseDataObject class]]

它应该能运行,我们的代码并不需要任何改变。

最后

另请注意,断点条件使用与'expr'或'print'命令相同的机制运行。因此,尝试断点命令的最简单方法是设置一个无条件断点,点击它,然后转到 lldb 控制台并使用 'print' 来玩,直到你得到一些有用的东西。

Responses