我花了一些时间调试这个问题,结果被难住了。
我正在尝试将自定义对象紧密地打包到NSData
中,以在将它们保存到磁盘时节省空间。我已经为这些自定义对象实现了自定义编码和解码方法。当我打包和解包对象时,一切都会根据调试器正确初始化。代码运行后,它立即具有EXC_BAD_ACCESS code=1
或code=2
。
以下是编码和解码:
-(instancetype) initWithData:(NSData *)data {
self = [super init];
if (self) {
_memConstants = [[DWMemoryConstants alloc]init];
int arraySize = _memConstants.chunkSize * _memConstants.chunkSize;
NSData* unencodedBlocks[arraySize];
[data getBytes:unencodedBlocks];
_blocks = [[NSMutableDictionary alloc]init];
for (int i = 0; i < 10; i++) {
DWBlock *block = [[DWBlock alloc]initWithData:unencodedBlocks[i]];
[_blocks setObject:block forKey:[NSValue valueWithCGPoint:CGPointFromString(block.blockName)]];
if (i == 0) {
_position = CGPointFromString(block.chunkName);
}
}
_chunkName = NSStringFromCGPoint(_position);
_bounds = CGRectMake(_position.x * _memConstants.chunkSize * kBlockSpriteWidth,
_position.y * _memConstants.chunkSize * kBlockSpriteWidth,
_memConstants.chunkSize * kBlockSpriteWidth,
_memConstants.chunkSize * kBlockSpriteWidth);
}
return self;
}
-(NSData *) customEncode {
NSMutableData *data = [[NSMutableData alloc]init];
NSData* blocks[_blocks.allValues.count];
int count = 0;
for (DWBlock *block in _blocks.allValues) {
blocks[count] = [block customEncode];
count++;
}
[data appendBytes:&blocks length:sizeof(blocks)];
return data;
}
异常发生在该代码的末尾。它首先成功地打印了所有的日志消息,我已经验证了Chunk对象没有任何问题:
-(void) testEncodeDecode {
DWChunk *testChunk = [[DWChunk alloc]initWithPosition:CGPointMake(100, 100)];
NSData *testData = [testChunk customEncode];
NSLog(@"datasize= %@", testData);
DWChunk *unencodedChunk = [[DWChunk alloc]initWithData:testData];
NSLog(@"blocks=%@", unencodedChunk.blocks.allValues);
NSLog(@"this message will print");
}
正如我所说,Chunk和Block对象的所有变量都已正确初始化。我怀疑这个问题与物体释放不当有关,但我不知道该怎么办。
编辑:我已经启用了僵尸检查,现在我收到了以下错误消息:
2014-10-24 11:38:31.775 DigWorld[10622:60b] *** -[NSConcreteMutableData release]: message sent to deallocated instance 0x81790710
编辑:我最初的想法在技术上是错误的(读代码太快…),但实际的错误是完全相同的。通过保存blocks
,您只保存指针数组,而不保存实际数据。你可以确保:在你的评论中,你说sizeof(blocks)=3600
和_blocks.allValues.count=900
,所以你只保存900个指针(*4字节/指针=3600字节)
由[block customEncode]
创建的实际数据然后由ARC处理
之后读取此数组时,您将获得指向已标记为已释放的内存片段(以前的NSData
)的指针,从而导致崩溃,并解释启用僵尸时的错误消息。
棘手的部分:由于这是程序的同一个实例,并且编码和解码是相邻运行的,因此实际内存没有被重新分配/覆盖。因此,您会得到对象保存正确的印象。但请放心,这段记忆最终会被其他东西覆盖。您可以通过将NSData
保存在磁盘上并在重新运行程序时对其进行解码来确保这一点。
在我看来,你似乎在试图重新发明这里的轮子。NSCoding
协议正是为了这个目的而创建的:在磁盘上持久化对象的图形。NSKeyedArchiver
类将在一个经过良好测试和文档化的界面中,以灵活性和平台独立性为您提供磁盘串行化。
您应该利用该体系结构。即使您认为NSKeyedArchiver
创建的归档对于您的目的来说太"大",也要使用现有的体系结构并创建自己的归档器。
如果你选择这样做,你可以从这个开源的NSCoder
子类实现中获得灵感(免责声明:我写的)