【问题标题】:How to set a breakpoint on "objectAtIndex:" method of a specific property in a specific class in XCode 4?如何在 XCode 4 中特定类中特定属性的“objectAtIndex:”方法上设置断点?
【发布时间】:2012-11-28 04:13:48
【问题描述】:

我想在特定类中特定属性的“objectAtIndex:”方法上设置一个符号断点。

见以下代码:

@interface Foo
...
@property (strong,nonatomic) NSMutableArray *fooArray;
...
@end

我尝试了以下方法:

  • -[[Foo fooArray] objectAtIndex:]
  • -[Foo::fooArray objectAtIndex:]
  • -[Foo::fooArray objectAtIndex]
  • Foo::fooArray::objectAtIndex:
  • Foo::fooArray::objectAtIndex
  • Foo::fooArray::objectAtIndex()

这些解决方案都不起作用。

有什么办法吗?

【问题讨论】:

  • Objective-C 不是 C++。试试conditional breakpoints
  • 我不明白条件断点如何帮助我。如果我在“addObject”方法上设置断点,问题是一样的。我不知道这个方法的所有可能调用在哪里。
  • 大多数示例的问题是“Foo::fooArray”在 Objective-C 中没有任何意义。最后三个对于引用 C++ 方法可能有效(或多或少),但它们不描述 Objective-C 方法。 -[someClass objectAtIndex] 是有效的,但不是您的意思,因为 objectAtIndex:objectAtIndex 是不同的选择器——它们有效地引用了不同的方法。

标签: objective-c xcode xcode4 breakpoints


【解决方案1】:

不幸的是,虽然这很有用,但由于多种原因,它无法工作。

第一个涉及如何指定方法。用于识别断点中方法的方法签名包含三个部分:

-¹[NSDictionary² objectForKey:³]
+¹[NSString² stringWithContentsOfURL:encoding:error:³]
  1. 这是实例方法 (-) 还是类方法 (+)?
  2. 哪个类实现了这个方法?
  3. 这个方法的选择器是什么?

所以你的第一个问题是你为#2写的不是一个类。

您所拥有的是一个类,后跟一个属性名称以某种方式。这是行不通的,因为调试器无法知道这是否是纯访问器——它不能确定您或任何实现该属性的人没有编写自定义访问器来执行其他操作。这意味着调试器没有良好、可靠的方法来获取该值,或者知道该值何时发生变化,而不会产生潜在的副作用。

此外,类在方法签名中的作用是确定哪个类提供了您设置断点的实现。当您开始尝试引用包含对象的属性时,它就会消失,因为调试器 需要 一个类,并且必须从对象中获取它 - 请参阅前面的段落说明了始终知道哪个对象存在的一些困难。

(公平地说,调试器确实可以监视实例变量的值—IIRC,两个调试器都已经可以在观察点中执行此操作,尽管我上次尝试观察点时观察点的可靠性很不稳定。如果调试器可以将属性转换为它的支持 ivar,如果它有一个,并且观察它,那么对于大多数属性来说,这将是一个不错的 90% 解决方案,这些属性不受富有想象力的存储实现和自定义访问器的支持。但是调试器今天不能这样做。)

第二个原因是 NSArray 是一个类簇。

您可能已经知道第一部分(我怀疑这就是您尝试通过另一个属性指定单个对象的原因):

NSArray 和 NSMutableArray 都是抽象类,这反过来又意味着两者都没有实现作为数组的业务;每一个都实现了一堆方便的方法,同时留下一组未实现的核心方法,供子类实现。

所以,当你创建一个 NSArray 时,你并没有创建一个 NSArray。您返回的对象将是 NSArray 的某个私有子类的实例,它自己实现了如何管理有序对象列表的所有细节。

所以你可以在-[NSArray objectAtIndex:] 上设置一个断点,但它永远不会被命中,因为没有任何东西使用 NSArray 的objectAtIndex: 实现——使用那个实现是没有意义的,因为那个实现会引发异常(旨在捕获忘记实现它的子类)。

打断你问题的部分是:

虽然 NSArray 的各种非必要方法的实现最终是根据核心方法定义的,例如 objectAtIndex:,但这并不意味着子类必须使用这些实现。如果objectAtIndex: 不是最有效的方式(例如,如果数组由链表而不是 C 数组支持,则子类很可能有自己的不使用 objectAtIndex: 的实现) )。

所以,总结一下这个长长的答案:

  • 调试器无法可靠地监视属性的值。
  • 因此,当调用作为该属性值的对象类中的方法时,调试器不可能中断,因为设置断点的正确方法可能随时更改,并且调试器无法知道何时发生。
  • 即使您可以在objectAtIndex: 上中断某个由属性标识的对象,该数组也可能永远不会使用objectAtIndex:,在这种情况下,您的断点无论如何都不会被命中。

您可能应该通过打破objectAtIndex: 来询问有关您正在尝试做的任何事情的另一个问题。我假设您正在尝试调查应用中的错误;这个错误可能是另一个有趣的问题。

【讨论】:

  • 感谢您的解释!那很清楚。我忘记了像“-/+[]”这样的符号断点的第 2 部分必须是一个类。但是,我很失望没有办法在属性的方法上添加断点!即使我的问题涉及 NSArray 的“objectAtIndex:”方法,对于任何其他属性的方法(例如,对于像 AnotherFoo* foo 这样的属性),问题都是相同的。 -[AnotherFoo fooMethod:] 可以解决我的问题,但我想通过指定属性来过滤断点。无论如何,@n-b 可能已经找到了解决方案!
【解决方案2】:

经过一番挖掘,我找到了解决这个问题的方法。有点丑。

它涉及在由第一个断点触发的命令中动态创建条件断点。

首先,只要你的 fooArray 准备好就中断。我选择了fooArray 访问器,但它可以更早完成:

breakpoint set --name "-[Foo fooArray]"

然后,当在这个特定的数组对象上调用objectAtIndex: 时,你想要的是中断。首先让我们把它的指针放在一个变量中:

expr id $watch = self->_fooArray

然后创建一个新的断点,在条件中使用这个变量:

breakpoint set --name "-[__NSArrayI objectAtIndex:]" --condition "$rdi == $watch"

  • $rdi 包含 self,至少在 x86_64 上。在 ARM 上使用 $r0。 (请参阅 Clark Cox 的 great post on the topic。)
  • -[NSArray objectAtIndex:] 永远不会被调用。正如 Peter 提到的,NSArray 是一个类集群,而您的数组实际上是一个 __NSArrayI

或者,在 Xcode 中:

(不要忘记选中“继续”框。)

它不是很漂亮,但它似乎工作!

【讨论】:

  • 不错!我尝试将它与 lldb 一起使用。目前它不能识别 $rdi。我搜索解决方案。
  • 如果你在 ARM 上,self should be in $r0.
【解决方案3】:

我不在我的 Mac 上,所以我不能自己尝试这个,但是怎么样:

breakpoint set -n "-[[Foo fooArray] objectAtIndex:]" 

【讨论】:

  • 将此用于“-[UIScrollView removeFromSuperview]”,效果很好,谢谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-05
  • 2018-12-15
相关资源
最近更新 更多