0

我需要一些关于使用 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;
}

如果我注释掉数据的编码器和解码器行,我会为其他所有内容返回正确的值,但我也需要数据。

4

1 回答 1

1

好的,这里有几个不同的问题。值得庆幸的是,如果您了解 C,它们大多很容易修复。(顺便说一句,您可能应该花一些时间 [重新] 学习 C,因为我的印象是您到目前为止只是掌握了 C 的基础知识。 Objective-C 并且对它的超集语言了解不多。)无论如何,除了其中一个之外,所有这些都与特定行有关,所以我将它复制到这里并将其砍掉,以便更容易检查:

self.templateData =
    (const char *)[decoder
                   decodeBytesForKey:@"templateData"
                   returnedLength:(NSUInteger *)self.templateSize];
  1. 尽管有一个更明显的问题,但可能导致这次特别崩溃的问题是:(NSUInteger *)self.templateSize. 我猜你试图获取templateSize属性实例变量的地址,但这不起作用。如果您想传入该实例变量的地址,您实际上需要做的事情类似于&_instanceVariableor &this->_instanceVariable(前者通常有效,因为 Obj-C 允许不合格的实例变量访问)。(注意:&最后两个代码位中的一元是地址运算符。)

    您编写的代码是错误的,因为它没有获取该属性的基础实例变量的地址,它只是将返回的大小转换为指针。所以如果返回0,则指针为0;如果返回4,则指针为4;如果它返回 42801,则指针是 42801(即,它只是指向那些将指向的任何东西,第一个是 NULL)。因此,最后,decodeBytesForKey:returnedLength:正在尝试写入可能是也可能不是可用内存的地址。谢天谢地,这很容易解决。

  2. 您可以修复#1,仅此一项就应该让它在技术上工作很短的时间,但它仍然是错误的。您还必须考虑到由返回的缓冲区decodeBytesForKey:returnedLength:是一个临时缓冲区——它只在编码器存在时才存在。这在NSKeyedUnarchiverdecodeBytesForKey:returnedLength:的文档的讨论部分和 NSCoder 文档中有所提及 decodeBytesWithReturnedLength:。不幸的是,如果您没有找到完全正确的位置,则可能很容易错过该细节,但它返回一个 const 指针可能是结果具有临时生命周期的证据。无论如何,如果要保留该数据,则需要复制返回的缓冲区。

    您在聊天中提到该templateData属性本身是 a char *,因此这意味着您可能想要分配一个相同大小的新内存块、memcpy缓冲区并存储它。这显然与您知道也要释放缓冲区的假设相吻合。

    到目前为止,更简单的方法是使用 NSData 而不是指向某些内存的指针。您仍然可以编码为字节(这允许您给它一个密钥)并解码为字节,但您可以让 NSData 处理复制缓冲区并为您释放它。例如,

    NSUInteger size = 0;
    const char *buffer = [coder decodeBytesForKey:@"key" returnedLength:&size];
    NSData *data = [NSData dataWithBytes:buffer length:size];
    

    有了这个, NSData 对象和 ARC 反过来为你处理。根据您的具体需求,这可能不是可取的,因此显然要确定对您的具体情况重要的内容。

  3. 这不是一个特定的问题,而是更多的冗余问题:您不必在对字节本身进行编码之前对字节的长度进行编码。这已经由编码器处理,因此它通过returnedLength:参数 ( lengthp) 返回其长度。因此,不需要对长度进行编码和解码的行。

于 2014-06-16T21:24:32.663 回答