将NSValue解码为b2fixture时的EXC_BAD_ACCESS

EXC_BAD_ACCESS when decoding NSValue into b2fixture

本文关键字:EXC BAD ACCESS 时的 b2fixture NSValue 解码      更新时间:2023-10-16

解决这个问题已经有一段时间了。我使用的是box2D,并将b2fixture编码为NSValue。当我使用解码时出现问题

[value getValue:&fixture];

2012年,这个代码在我的旧mac上运行得很好,但在过去的4年里,矩阵中似乎发生了一些事情!

以下是编码和解码的相关代码:

vertGrid = [[NSMutableArray alloc] initWithCapacity:(NSUInteger) xUnits];
for (int x = 0; x <= xUnits; x++) {
    b2Fixture *fixture = ... (not relevant)
    NSValue *wrappedValue = [NSValue value:&fixture withObjCType:@encode(struct b2Fixture)];
    [vertGrid addObject:wrappedValue];

}
//test I can unwrap the vertGrid
for (NSValue *value in vertGrid) {
    b2Fixture *fixture;
    [value getValue:&fixture];
    NSLog(@"yo yo yo");
}

如果它有效,那么纯属偶然。您没有包装内容,而是意外地复制了指针。内存管理和内存对齐的变化现在可能会"打破"它(32位对64位?)。

无论如何,你的包装和展开都是错误的,因为两者都有额外的(不正确的)间接性。让我们从包装值开始。您的代码是:

b2Fixture *fixture = ... (not relevant)
NSValue *wrappedValue = [NSValue value:&fixture withObjCType:@encode(struct b2Fixture)];

-[NSValue value:withObjCType:]的文档提供了必要的信息:

value:指向要存储在新值对象中的数据的指针。

由于您希望存储数据,因此需要传递struct b2Fixture *,因为该方法将从该内存位置读取数据,但您传递的是struct b2Fixture **。因此,将复制错误的数据(指针而不是数据)。

这里有另一个关于这种方法实际工作方式的提示:

此方法与valueWithBytes:objCType:具有相同的效果,并且可能在将来的版本中被弃用。您应该使用valueWithBytes:objCType:

换句话说:方法复制内容,而不是指向内容的指针。方法名称并没有说明这一点,这就是为什么使用valueWithBytes:objCType:是一个更好的选择。

因此,您包装价值的调用必须是:

NSValue *wrappedValue = [NSValue value:fixture withObjCType:@encode(struct b2Fixture)];

(注意缺少&)。

接下来,展开。您有:

b2Fixture *fixture;
[value getValue:&fixture];

再次,让我们看看-[NSValue getValue:]的文档,它说:

buffer:将值复制到其中的缓冲区。缓冲区必须足够大以容纳值

这意味着您需要分配存储。这里有两种选择:按库存分配或按堆分配。

// Allocate on the stack:
b2Fixture fixture;
[value getValue:&fixture];
// Allocate on the heap:
b2Fixture *fixture = malloc(sizeof(b2Fixture));
[value getValue:fixture]; // No "&"!
...
free(fixture);

评论后更新:

由于您似乎正在尝试包装一个C++对象,因此需要采取不同的做法。我认为有两种方法可以解决这个问题:

  • 增强对象,使其提供二进制表示并序列化。添加一个可以反序列化二进制表示的构造函数
  • 使用+[NSValue valueWithPointer:]-[NSValue pointerValue]。这假设对象位于堆上,并将使内存管理更加复杂