取消引用 NSCoder 的decodeBytesForKey 返回的指针时,iOS 设备崩溃
当使用 Apple LLVM 编译器 3.0 并使用 -O3 进行编译时,我发现 NSCoder 出现了异常崩溃。它只会在设备上崩溃。我测试过运行 iOS 5 的 iPhone 4、运行 iOS 5 的 iPad 2 和运行 iOS 4 的 iPad 1。所有崩溃情况都是相同的。以下是相关代码部分:
-(id)initWithCoder:(NSCoder*)decoder
{
if (![super init])
{
return nil;
}
NSUInteger length = 0;
uint8_t* data = (uint8_t*)[decoder decodeBytesForKey:BBKey returnedLength:&length];
m_value = *(BBPointI32*)data;
return self;
}
BBPointI32 的含义如下:
typedef struct
{
NSInteger x;
NSInteger y;
}
BBPointI32;
当 data
取消引用时,EXC_BAD_ACCESS
就会发生。这不是空指针问题。如果我附上GDB,我可以看到长度是8,sizeof(BBPointI)也是8,数据是正确的。
如果我查看反汇编,崩溃发生在:
ldrd r2, r3, [r0]
看起来不错。 r0包含0xb546e,这是data
的地址。当我检查该内存时,我可以看到它包含我期望的数据。对于感兴趣的人,r2 包含 72(不确定那是什么),r3 包含 8(很可能是 length
的值)。
谁能解释一下这个问题?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
ldrd 需要地址是 8 字节对齐的。 *(BBPointI32 *)data 习惯用法并不安全,因为数据不是 8 字节对齐的。使用 memcpy 将字节获取到结构中。
ldrd needs the address to be 8-byte aligned. The *(BBPointI32 *)data idiom is not safe since data is not 8-byte aligned. Use memcpy to get the bytes into the struct instead.
你用的是ARC吗?如果是这样,我认为问题在于编译器可以在
decodeBytesForKey:
调用之后自由释放decoder
(因此释放返回值指向的缓冲区)。这是相同的 内部指针问题垃圾收集有。您可以
CFRetain
/CFRelease
解码器来延长其生命周期,或者只需稍后在方法中添加[decoder self]
即可使其保持活动状态直到那时。我怀疑您也可以通过用
__attribute__((objc_precise_lifetime))
注释decoder
来解决这个问题,但是我对 该属性有点模糊。Are you using ARC? If so, I believe the issue is that the compiler is free to release
decoder
after thedecodeBytesForKey:
call (hence releasing the buffer to which the return value pointed).It's the same interior pointer issue garbage collection has. You can
CFRetain
/CFRelease
your decoder to extend its lifetime, or else just add a[decoder self]
later in the method to keep it alive until that point.I suspect you might also be able to solve this problem by annotating
decoder
with__attribute__((objc_precise_lifetime))
, but my understanding of that attribute is somewhat hazy.你的例子留下了很多变量供任何潜在的帮助者质疑。例如:如果这个解档器有一些奇怪的东西怎么办?内存管理是否正确?
我能够重现您所看到的崩溃,并且可以确认它仅在启用 -O3 时发生,而不是在选择“无”进行优化时发生。这是崩溃代码的减少,消除了外部变量,例如编码器的内存管理等。请注意,下面的代码有意保留所有对象,以消除崩溃与意外过度释放或侧面相关的可能性。使用 ARC 的效果,正如 Andy 在另一个答案中所建议的:
我认为这对问题给出了更简洁的描述,任何想要提供帮助的人都可以将其用作深入研究的基础。到目前为止,我的预感是这是一个优化错误LLVM 3.0,但也许其他人会对正在发生的事情有更好的理论。
您在问题中没有提到但我在设备崩溃中注意到的一点是,该故障伴随着 EXC_ARM_DA_ALIGN 错误的提及,作为错误访问异常的原因。我在谷歌上搜索了一篇博客文章,它似乎暗示了相同的症状,并且可能导致崩溃,正如您在这里看到的那样:
http://www.galloway.me.uk/2010/10/arm-hacking-exc_arm_da_align-exception/
事实上,通过将上面的行更改
为
崩溃行为似乎得到了缓解,并且代码的行为符合预期。
Your example leaves a lot of variables to be questioned by any potential helper. For example: what if there's something funky with this unarchiver? Is memory being managed correctly?
I was able to reproduce the crash you're seeing, and can confirm it only occurs when -O3 is enabled, and not when e.g. None is selected for optimization. Here is a reduction of the crashing code that eliminates outside variables such as memory management of the coders, etc. Note the code below purposefully retains all objects to eliminate the possibility that the crash is related to an accidental over-release, or a side-effect of using ARC, as suggested by Andy in another answer:
I think this gives a more succinct description of the problem that anybody who wants to help can use as a basis for diving in. My hunch thus far is this is an optimization bug in LLVM 3.0, but maybe somebody else will have a better theory about what is going on.
A point that you didn't mention in your question, but which I notice in the crash on my device, is the failure is accompanied by mention of a EXC_ARM_DA_ALIGN error as the reason for the bad access exception. I Googled a blog post that seems to allude to the same symptoms and probably cause for crashing as you are seeing here:
http://www.galloway.me.uk/2010/10/arm-hacking-exc_arm_da_align-exception/
Indeed, by changing the line above
to
The crashing behavior seems to be alleviated and the code behaves as expected.
我到达这个线程,谷歌搜索“EXC_ARM_DA_ALIGN”和“EXC_BAD_ACCESS”。其他答案都没有帮助我,因为这个错误是由于相对简单的事情而出现的。我写道:
即我在字符串文字前面留下了一个@。把它放回去解决了错误。
I got to this thread, googling "EXC_ARM_DA_ALIGN" and "EXC_BAD_ACCESS". None of the other answers helped me, as this error cropped up because of something relatively simple. I had written:
i.e. I had left off a @ in front of a string literal. Putting it back in solved the error.