我尝试调试iOS上的崩溃,该崩溃在支持A64 instruction set的设备上一致地重现。特别是iPad使用A7 / A8X SoC&#39。在任何32位iPad上运行时,完全相同的代码也将始终不崩溃(如果我将构建限制为仅32位体系结构然后在一个32位代码上运行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_msgSend
和performSelector:withObject:
在这种情况下应该基本相同。
那么为什么一个崩溃(仅在使用A64时)而另一个不崩溃?
当崩溃发生时,尝试调试崩溃时会出现下一个令人困惑的事情。 self
和object
都是NSManagedObject
个实例,我可以在调试器中观察到它们都是有效对象。但是,例外情况总是报告为:
-[NSManagedObjectContext entity]: unrecognized selector sent to instance 0x...
此呼叫显示为源自CoreData
的内部,我无法对如何发生这种情况做出任何合理的解释,特别是作为切换的副作用从32位到64位架构/构建。
对于导致此类问题的原因有什么想法吗?或者我应该选择那个performSelector:withObject:
并且开心吗?
答案 0 :(得分:2)
并没有特别想要触发它的代码
直接拨打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以了解如何使警告静音。如果这些物品可以返回物体,那么你需要确保它们不能留在它们身上,并且这只是一个兔子洞,你可能不应该倒下(或者至少应该作为一个新问题)。