我需要使用NSKeyedUnarchiver unarchiveObjectWithFile对decodeBytesForKey进行一些帮助。我似乎正在正确地编写所有内容但是读取数据会给我一个EXC_BAD_ACCESS错误。我试图对数据进行反序列化,并对各种答案和示例感到困惑。有人可以指出我做错了吗?
for (NSString *item in directoryContent){
if ([[item pathExtension] isEqualToString:@"template"]) {
**// Next line is the problem line from the calling code:**
templateToRead = [[NSKeyedUnarchiver unarchiveObjectWithFile:[documentsDirectory stringByAppendingPathComponent:item]] mutableCopy];
IDImageTemplate *template = [[IDImageTemplate alloc] init];
template.templateData = templateToRead.templateData;
template.templateQuality = templateToRead.templateQuality;
template.templateSize = templateToRead.templateSize;
template.templateLocation = templateToRead.templateLocation;
[templatesRead addTemplate:template];
}
}
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeInteger:self.templateSize forKey:@"templateSize"];
[encoder encodeBytes:(const unsigned char*)self.templateData length:self.templateSize forKey:@"templateData"];
[encoder encodeInt:self.templateQuality forKey:@"templateQuality"];
[encoder encodeInt:self.templateLocation forKey:@"templateLocation"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self) {
self.templateSize = [decoder decodeIntegerForKey:@"templateSize"];
**// Next line is where the real problem is:**
self.templateData = (const char *)[decoder decodeBytesForKey:@"templateData" returnedLength:(NSUInteger *)self.templateSize];
self.templateQuality = [decoder decodeIntForKey:@"templateQuality"];
self.templateLocation = [decoder decodeIntForKey:@"templateLocation"];
}
return self;
}
如果我注释掉数据的编码器和解码器行,我会为其他所有内容返回正确的值,但我也需要数据。
答案 0 :(得分:1)
好的,这里有一些不同的问题。值得庆幸的是,如果你知道C,他们很容易修复。(顺便说一下,你应该花一些时间[重新]学习C,因为我得到的印象是你到目前为止刚刚得到的关于Objective-C的基础知识,并且没有对它作为超集的语言学到很多东西。)无论如何,除了其中一个以外的所有语言都与特定的行有关,所以我会复制它在这里砍下它以便更容易检查:
self.templateData =
(const char *)[decoder
decodeBytesForKey:@"templateData"
returnedLength:(NSUInteger *)self.templateSize];
虽然有一个更明显的问题,但可能造成这种特别崩溃的问题是:(NSUInteger *)self.templateSize
。我猜您试图获取templateSize
属性的实例变量的地址,但这不会起作用。如果你想传递那个实例变量的地址,你实际需要做的就是&_instanceVariable
或&this->_instanceVariable
(前者通常有用,因为Obj-C允许非限定的实例变量访问)。 (注意:最后两个代码位中的一元&
是地址运算符。)
您编写的代码是错误的,因为它没有获取该属性的基础实例变量的地址,它只是将返回的大小转换为指针。所以如果它返回0,则指针为0;如果它返回4,则指针为4;如果它返回42801,则指针为42801(即,它只是指向那些指向的东西,第一个是NULL)。因此,最后,decodeBytesForKey:returnedLength:
正在尝试写入可能或可能不是可用内存的地址。谢天谢地,这很容易解决。
你可以修复#1而这一点应该让它在技术上工作很短的时间,但它仍然是错的。您还必须考虑到decodeBytesForKey:returnedLength:
返回的缓冲区是一个临时缓冲区 - 只有编码器存在时它才会存在。在decodeBytesForKey:returnedLength:
NSKeyedUnarchiver的文档和NSCoder decodeBytesWithReturnedLength:
文档的讨论部分中提到了这一点。不幸的是,如果您没有找到正确的位置,那么这个细节可能很容易被遗漏,但它返回一个const指针可能是结果具有临时寿命的证据。无论如何,如果要保留该数据,则需要复制返回的缓冲区。
你在聊天中提到templateData
属性本身就是char *
,这意味着你可能想要分配一个相同大小的新内存块,{{ 1}}缓冲区,并存储它。这显然是假设您知道也释放缓冲区。
到目前为止,更简单的方法是使用NSData而不是指向某些内存的指针。你仍然可以编码为字节(允许你给它一个键)并解码为字节,但你可以让NSData处理复制缓冲区并为你释放它。如,
memcpy
然后,NSData对象和ARC依次为您处理。根据您的具体需求,这可能不是更可取的,因此请明确确定对您的具体案例有何重要意义。
这不是一个特定的问题,而是冗余的事情:在编码字节本身之前,您不必编码字节的长度。这已由编码器处理,因此它通过NSUInteger size = 0;
const char *buffer = [coder decodeBytesForKey:@"key" returnedLength:&size];
NSData *data = [NSData dataWithBytes:buffer length:size];
参数(returnedLength:
)返回其长度的原因。因此,编码和解码长度的行是不需要的。