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 を知っていれば、ほとんどの場合簡単に修正できます。 Objective-C と、そのスーパーセットである言語についてはあまり学習していません。) とにかく、これらの 1 つを除くすべてが特定の行に関係しているため、ここにコピーして切り詰めて、検査しやすくします。

self.templateData =
    (const char *)[decoder
                   decodeBytesForKey:@"templateData"
                   returnedLength:(NSUInteger *)self.templateSize];
  1. もっと明白な問題がありますが、この特にクラッシュを引き起こしている可能性が高いのはこれです: (NSUInteger *)self.templateSize. プロパティのインスタンス変数のアドレスを取得しようとしていたと思いますがtemplateSize、これは機能しません。そのインスタンス変数のアドレスを渡したい場合に実際に行う必要があるのは、&_instanceVariableorのようなものです&this->_instanceVariable(Obj-C では非修飾インスタンス変数へのアクセスが許可されているため、通常は前者が機能します)。(注:&最後の 2 つのコード ビットの単項は、アドレス演算子です。)

    そのプロパティの基になるインスタンス変数のアドレスを取得していないため、記述したコードは間違っています。返されたサイズをポインターにキャストしているだけです。したがって、0 を返す場合、ポインターは 0 です。4 を返す場合、ポインターは 4 です。42801 が返された場合、ポインターは 42801 です (つまり、ポインターが指すものを指しているだけで、最初は NULL です)。したがって、最終的にdecodeBytesForKey:returnedLength:は、使用可能なメモリである場合とそうでない場合があるアドレスに書き込もうとしています。ありがたいことに、それは簡単に修正できます。

  2. #1を修正することができ、これだけで技術的に短時間動作するはずですが、それでも間違っています. decodeBytesForKey:returnedLength:によって返されるバッファが一時バッファであることも考慮する必要があります。これは、 のNSKeyedUnarchiverに関するドキュメントのディスカッション セグメントとNSCoderdecodeBytesForKey:returnedLength:のドキュメントで言及されています。残念ながら、正確に正しい場所を見ていないと、その詳細を見落としがちですが、const ポインターを返すことは、結果が一時的な寿命を持っていたという証拠になる可能性があります。とにかく、そのデータを保持したい場合は、返されたバッファのコピーを作成する必要があります。 decodeBytesWithReturnedLength:

    templateDataプロパティ自体がであるとチャットで述べた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 に答える