EXC_BAD_ACCESS 将 NSValue 解码为 b2fixture 时

EXC_BAD_ACCESS when decoding NSValue into b2fixture

这个问题已经有一段时间了。我正在使用 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");
}

如果成功了,那纯属偶然。您没有包装内容,而是不小心复制了指针。内存管理和内存对齐的变化现在可能 "break"(32 位与 64 位?)。

无论如何,您的包装和展开都是错误的,因为它们都有一个额外的(不正确的)间接寻址。让我们从包装价值开始。您的代码是:

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

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

value: A pointer to data to be stored in the new value object.

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

这是关于此方法实际工作原理的另一个提示:

This method has the same effect as valueWithBytes:objCType: and may be deprecated in a future release. You should use valueWithBytes:objCType: instead.

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

因此您对值进行包装的调用必须是:

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

(注意缺少的&)。

接下来,展开。你有:

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

再次,让我们看一下 documentation of -[NSValue getValue:],它说:

buffer: A buffer into which to copy the value. The buffer must be large enough to hold the value

这意味着您需要分配存储空间。您在这里有两个选择:在库存上或在堆上分配。

// 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:] and -[NSValue pointerValue]。这假定对象存在于堆上,并且会使内存管理更加复杂。