6

ここでミュータブル配列を使用しているという事実とは関係がないことを願っていますが、これは私を困惑させているので、そうであったとしても驚かないでしょう。

バックグラウンド:

私は小さなデータベースを作成しました。これは基本的に、recordObjects と呼ばれるカスタム オブジェクトを含む NSMutableArray です。アレイをセットアップしました:

database = [[NSMutableArray alloc] init];

そして、「recordObject」と呼ばれる私のカスタム オブジェクトには、次の変数と初期化が含まれています。

NSString *name;
int       anInt;
Bool      aBool;

メソッドも合成したので、次のような呼び出しを行うことができます。

aString = [[database objectAtIndex:someIndex] name];

また、表示する個々のレコードを追加、削除、および選択するためのメソッドをコントローラー クラスに追加しました。これまでのところ、すべてが正しく、正確に期待どおりに機能しています。

次に、recordObject クラス (NSObject のサブクラス) を NSCoder を使用するように設定し (@interface ディレクティブに含めることにより)、実装ファイルに次のカスタム エンコーダーおよびデコーダー メソッドを追加しました。

-(void) encodeWithCoder: (NSCoder *) encoder {
    [encoder encodeObject: name  forKey: @"recordName"];
    [encoder encodeInt:    anInt forKey: @"recordInteger"];
    [encoder encodeBool:   aBool forKey: @"recordBool"];
}

-(id) initWithCoder: (NSCoder *) decoder {
    name    = [decoder decodeObjectForKey: @"recordName"];
    anInt   = [decoder decodeIntForKey:    @"recordInteger"];
    aBool   = [decoder decodeBoolForKey:   @"recordBool"];
}

ファイルを書き込むために、次のものを使用しました。

[NSKeyedArchiver archiveRootObject:database toFile:myPath];

プログラムを実行すると、すべてが正しく動作するように見えます。配列内のレコードごとにエンコーダー メソッドが呼び出され、ファイルがディスクに書き込まれます。TextEdit でファイルを開くと、データがそこにあることがわかります (ほとんどわかりませんが)。

問題:

ここで、ひっかかります。

次のコードを追加して、ファイルをデータベース配列にロードしました。

database = [NSKeyedUnarchiver unarchiveObjectWithFile:myPath];

プログラムを再度実行すると、今度はデータベースをロードすると、正しく動作するように見えます。私の最初のテストは、NSMutableArray カウント メソッドを使用することでした。

x = [database count];

その結果、X にはファイル内の正しい数のレコードが入力されました。データベースを保存したときに 5 つのレコードがあった場合、プログラムの次の実行時にデータベースをロードした後、X は 5 に設定されます。

さて、ここに大きな問題があります:

アクセサ メソッドのいずれかを使用しようとすると、プログラムがクラッシュします。たとえば、データベースをロードした後に次を使用しようとすると:

aString = [[database objectAtIndex:someIndex] name];

プログラムがクラッシュし、コンソールに次のエラーが返されます。

Program received signal:  “EXC_BAD_ACCESS”.
sharedlibrary apply-load-rules all

私の解釈では、何らかの理由でデータがデータベース配列に正しくロードおよび初期化されていませんが、私の人生では、ここでどこが間違っているのかわかりません。

補足として、私が実装したものはすべて、Stephen G. Kochan の本「Programming in Objective-C」からのものです。

どんなアイデアでも大歓迎です!

4

4 に答える 4

14

コードにはいくつかの問題があります。

あなたのinitWithCoder:方法は完全には実装されていません。[super init]に電話して戻る必要がありますselfcopyまたは文字列オブジェクトも必要ですretain。そうしないと、自動解放されます。

- (id)initWithCoder:(NSCoder *)decoder 
{
    self = [super init];
    if(self)
    {
         name    = [[decoder decodeObjectForKey: @"recordName"] copy];
         anInt   = [decoder decodeIntForKey:    @"recordInteger"];
         aBool   = [decoder decodeBoolForKey:   @"recordBool"];
    }
    return self;
}

もう1つの問題は、次の行にあります。

database = [NSKeyedUnarchiver unarchiveObjectWithFile:myPath];

それは問題ありませんが、次の 2 つの点を除きます。

  1. オブジェクトへの参照を保持していないため、自動解放されます。
  2. NSKeyedArchiverは不変オブジェクトを返します。この場合はNSArrayではなくNSMutableArrayです。

これを行う必要があります:

database = [[NSKeyedUnarchiver unarchiveObjectWithFile:myPath] mutableCopy];

これにより、オブジェクトが保持され(コピーであるため)、オブジェクトがNSMutableArray.

于 2012-04-30T23:57:16.190 に答える
4

-initWithCoder で recordObjects を初期化しているようには見えません。

次のようなことを試してください:

-(id) initWithCoder: (NSCoder *) decoder {

    self = [super init];

    if (self){

       name    = [decoder decodeObjectForKey: @"recordName"] copy];
       anInt   = [decoder decodeIntForKey:    @"recordInteger"];
       aBool   = [decoder decodeBoolForKey:   @"recordBool"];
     }

    return self;
}

アーカイブするとデータはそこにありますが、適切にアーカイブ解除していません。

于 2012-04-30T23:28:25.830 に答える
2

私にはメモリ管理の問題のように聞こえます。EXC_BAD_ACCESS通常、割り当てが解除されたオブジェクトにアクセスしようとしていることを意味します。自動解放されたオブジェクトを返すため、明示的に使用するか、保持されたプロパティに割り当てることにより、保持しunarchiveObjectWithFile:たい場合は保持する必要があります。retain

于 2012-04-30T23:26:47.267 に答える
0

カスタムオブジェクトのアーカイブを解除するときに同じ問題が発生しました

self.calTable = [[NSKeyedUnarchiver unarchiveObjectWithFile:calibrationFile] objectForKey:@"calTable"];

ロブの答えに基づいて、私はに変更しました

self.calTable = [[[NSKeyedUnarchiver unarchiveObjectWithFile:calibrationFile] mutableCopy] objectForKey:@"calTable"];

すべてのエラーを修正しました。

于 2014-08-07T19:12:39.030 に答える