1

私のコア データ ストアには、 "Test" という属性を持つ51Entry個のエンティティが含まれています。messageこれに対して NSFetchRequest を実行すると、それらがすべて存在することが確認されます。

ただし、メソッドには別の部分があります。これは、NSData の大きなチャンクを処理するためにメモリを集中的に使用するため、[oldContext reset];頻繁に呼び出す必要があります。

MOC から多くの NSData にアクセスするメモリ集約型のメソッドがあります。そのため、定期的に を呼び出します[oldContext reset];。この行がないと、メモリが不足します。

これを使用しても、正しい結果が返されないことがわかりました。これをテストするために、データ集約型のコードをコメント アウトし、属性を返すコードを残しました。そのうちのmessage51 個は "Test" に設定されています (別の NSFetchRequest で確認)。

ただし、使用[oldContext reset];すると、メッセージが「テスト」に設定された6つの結果しか返されません。これは私が使用しているコードです:

    NSFetchRequest *oldFetchRequest = [[NSFetchRequest alloc] init];
    NSEntityDescription *oldEntryEntity = [NSEntityDescription entityForName:@"Entry"
                                                      inManagedObjectContext:oldContext];
    [oldFetchRequest setEntity:oldEntryEntity];
    [oldFetchRequest setFetchBatchSize:10];
    [oldFetchRequest setIncludesPropertyValues:NO];
    NSArray *entrys = [oldContext executeFetchRequest:oldFetchRequest error:&error];

    int totalEntries = [oldContext countForFetchRequest:oldFetchRequest error:nil];

    int i = 0;

    while (i < totalEntries) {
        @autoreleasepool {

            Entry *entry = [entrys objectAtIndex:i];

            NSLog(@"message 1: %@", [entry valueForKey:@"message"]);


            [oldContext reset];


            i++;
        }
    }

なぜそれがすべき51の「テスト」結果を出さないのかについて何か考えはありますか?

4

3 に答える 3

1

Apple docs によると、これは ManagedObjectContext リセットが行うことです:

  • 受信側の管理オブジェクトはすべて「忘れられた」状態です。このメソッドを使用する場合は、後で無効になるため、レシーバーを使用してフェッチされた管理対象オブジェクトへの参照も必ず破棄する必要があります。

したがって、コードでは、while ループが繰り返されるたびにすべてのオブジェクトを忘れるように oldContext に指示しています。

NSData が MOC のどこから来ているかはわかりませんが、エントリ エンティティからのものである場合は、次の 2 つのオプションがあると思います。

1) MOC を毎回リセットする代わりに、NSManagedObjectContext のrefreshObject:mergeChanges:メソッドを使用します。これにより、エンティティが再フォールトし、メモリが解放されます。たとえば、次のようになります。

while (i < totalEntries) {
    @autoreleasepool {

        Entry *entry = [entrys objectAtIndex:i];

        NSLog(@"message 1: %@", [entry valueForKey:@"message"]);


        [oldContext refreshObject:entry mergeChanges:NO];


        i++;
    }
}

2) Core Data エンティティ内に大量のデータを保存しないでください。代わりに、データをファイル システムに書き出し、エンティティ内のデータのパスへの参照を保持します。それが私の好ましいアプローチです。「Large Data Objects」セクションの下にあるApple のCore Data Performanceを見てください。

于 2012-09-26T17:50:47.917 に答える
1

フェッチ バッチ サイズを現在の 10 ではなく 1 に設定してみてください。

この状況に対する私の解釈は次のとおりです。

  1. これによりexecuteFetchRequest...、コンテキストに 10 件のレコードがフェッチされます。
  2. ただし、コンテキストをリセットすると、すべてのレコードが失われます。つまり、10 カウントの最初のレコードのみです。コンテキストがリセットされる前に、最初のレコードが記録されることに注意してください。
  3. 合計で 51 のレコードがあるため、51 % 10 = 6レコードが残ります。
于 2012-09-26T12:33:11.950 に答える
1
NSFetchRequest *oldFetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *oldEntryEntity =
    [NSEntityDescription entityForName:@"Entry"
                inManagedObjectContext:oldContext];
[oldFetchRequest setEntity:oldEntryEntity];
[oldFetchRequest setFetchBatchSize:10];
[oldFetchRequest setIncludesPropertyValues:NO];
NSArray *entrys = [oldContext executeFetchRequest:oldFetchRequest error:&error];
int totalEntries = [oldContext countForFetchRequest:oldFetchRequest error:nil];

上記のコードは、 MOC でフェッチ リクエストを実行しoldContextます。その結果、entrys配列には、要求に一致するデータベース内の各オブジェクトの管理対象オブジェクトが含まれます。さらに、バッチ サイズを設定した結果、フェッチはオブジェクトのバッチ サイズに違反します。

何が起こっているかを確認するための実験として、これらのログ ステートメントを追加します...

NSLog(@"entrys count = %u", entrys.count);
for (NSManagedObject *entry in entrys) {
    NSLog(@"entry: %@", entry);
}

その配列に何が入っているか分かりますか? それは今意味がありますか?

残りのコードを見てみましょう。

int i = 0;
while (i < totalEntries) {
    @autoreleasepool {
        // You get the i-th entry.  It will be a managed object.  It could be a fault
        // or it could be a fully hydrated object.  Based on your batch size, the
        // first ten (0 <= i < 10) will be complete objects.
        Entry *entry = [entrys objectAtIndex:i];

        // Log the "message" attribute.  By calling valueForKey, the object will be
        // faulted into memory if it is a fault.  Since your batch size is 10,
        // this will make sure 10 objects are faulted if one is needed.
        NSLog(@"message 1: %@", [entry valueForKey:@"message"]);

        // Resetting the entire context blows away everything in the context.
        // Calling reset is a hard call, and should not be done if you have
        // references to the objects in the context.
        [oldContext reset];

        i++;
    }
}

別の方法をお勧めします。呼び出しresetは、オブジェクトを保持するコンテキスト向けには設計されていません。

いくつかの選択肢があります。子コンテキストを作成し、そこでスクラッチ作業を行ってから、コンテキストを解放できます。使用したすべてのメモリを解放します。

選択的に使用できます

- (void)refreshObject:(NSManagedObject *)object mergeChanges:(BOOL)flag

flagである場合、オブジェクトをフォルトに戻しNO、メモリを解放します。特に関係を管理している場合は、これにもいくつかの固有の危険性があることに注意してください。

他にもいくつかのオプションがありますが、最終的な目標が何であるかを知らなければ...何が最も有益かを判断するのはちょっと難しいです.

これらの呼び出しに関連するすべてのドキュメントを読むことを強くお勧めします。実際、Core Data は非常に洗練されていますが、重要な作業を行う場合に知っておくべき「壊れやすい」相互作用がいくつかあります。

すべての Core Data ドキュメントを読むことを強くお勧めします。プロジェクトで抱えていたすべての問題に対処します。

于 2012-09-26T13:43:33.240 に答える