【问题标题】:A64: objc_msgSend crashes while performSelector:withObject: worksA64:objc_msgSend 在 performSelector:withObject: 工作时崩溃
【发布时间】:2015-07-24 23:25:39
【问题描述】:

我正在尝试调试 iOS 上的崩溃,该崩溃在支持 A64 instruction set 的设备上始终如一地重现。特别是使用A7/A8X SoC 的 iPad。在任何 32 位 iPad 上运行时,完全相同的代码也将始终不会崩溃(如果我将构建限制为仅 32 位架构,然后在支持 64 位的 iPad)。

崩溃报告为EXC_BAD_ACCESS,触发它的代码没有什么特别花哨的:

if (object && [self respondsToSelector:addSelector]) {
    objc_msgSend(self, addSelector, object);                  //EXC_BAD_ACCESS on A64 devices!
    //[self performSelector:addSelector withObject:object];   //no crash  
}

违规行是objc_msgSend(self, addSelector, object);。第一个令人困惑的部分是,如果我用[self performSelector:addSelector withObject:object]; 替换这一行,一切都会正常运行(尽管它给我留下了令人讨厌的“PerformSelector 可能导致泄漏......”警告)。除非我完全误解了某些东西,否则objc_msgSendperformSelector:withObject: 在这种情况下应该基本上是等价的。

那么为什么一个会崩溃(并且仅在使用 A64 时)而另一个不会?

下一个令人费解的事情是在崩溃发生时尝试调试它。 selfobject 都是 NSManagedObject 实例,我可以在调试器中观察到它们都是有效对象。但是,异常总是报告为:

-[NSManagedObjectContext entity]: unrecognized selector sent to instance 0x...

调用显示为源自CoreData 的内部结构,我无法给出任何合理的解释来说明这是如何发生的,尤其是从 32 位切换到64 位架构/构建。

对于导致此类问题的原因有什么想法吗?还是我应该直接使用 performSelector:withObject: 并开心?

【问题讨论】:

  • 你使用的是 SQLite 数据库类型吗?
  • @WarrenBurton - 是的,该应用使用 NSSQLiteStoreType 和 CoreData。
  • 什么是addSelector
  • 如果可能,您可以更改为NSBinaryStoreType 并查看崩溃是否重现。并不是说这是一个长期的解决方案。只是想看看 SQLite 引擎是否存在故障。
  • @Cy-4AH - 它有些可变,但是addSelector 将始终是用于操作“对多”关系的“CoreDataGeneratedAccessor”方法,适用于entityobject被操纵。

标签: ios objective-c ipad 32bit-64bit


【解决方案1】:

触发它的代码并没有什么特别花哨的地方

直接调用objc_msgSend()花哨的,并且正确地做起来很棘手,正如你所发现的那样。

第一个令人困惑的部分是,如果我将这一行替换为 [self performSelector:addSelector withObject:object];,一切都会正常运行

是的。因为这些不一样。

objc_msgSend* 有多种风格,您需要根据返回类型和您的处理器选择正确的一种。具体分为三个版本:

  • objc_msgSend_fpret -- 对于浮点返回类型(适用于 OS X;我还没有查看它是否适用于 64 位 ARM)
  • objc_msgSend_stret -- 用于结构返回类型(如CGPoint
  • objc_msgSend -- 其他返回类型

如果这在所有处理器上总是完全正确,我不记得了。一些处理器对待“大”结构不同于“小”结构。而且调用约定都是特定于处理器的,这就是为什么您只能在一个处理器上看到它的原因。还记得我说过直接打电话给objc_msgSend() 很漂亮吗?

您使用它来调用任意选择器这一事实表明它们中的一些具有结构或浮点返回,在这种情况下,事情将在错误的寄存器中,事情将完全横向发展。

有关此问题的更多讨论,请参阅Why does the Objective-C compiler need to know method signatures?

对于导致此类问题的原因有什么想法吗?或者我应该只使用那个 performSelector:withObject: 并开心吗?

正如警告所说,performSelector: 可能会泄漏,因为 ARC 不知道如何对其进行内存管理。解决方案是重新设计它以使用块而不是选择器。如果您必须使用选择器,并且您确定此处调用的选择器都不会返回对象,请参阅https://stackoverflow.com/a/7933931/97337 了解如何使警告静音。如果这些可以返回对象,那么你需要确保它们不能有额外的保留,这只是一个你可能不应该去的兔子洞(或者至少应该作为一个新问题提出)。

【讨论】:

  • “您使用它来调用任意选择器的事实表明它们中的一些具有结构或浮点返回” - 返回类型始终为void,这应该意味着objc_msgSend使用正确的 API 吗?
  • 如果是这样,您可能不会只在一个平台上发生崩溃。它们都采用一个没有可变参数的参数吗?我会尝试构建将所有可能的选择器放入静态代码并编译它并查看程序集(助手中的“汇编器视图”)。确保它们都编译为完全相同的设置和函数调用。正如我所说,objc_msgSend 是棘手的并且是特定于平台的。很难做到正确。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-20
  • 2019-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-01
  • 2011-10-16
相关资源
最近更新 更多