6

10.000以上のNSManagedObjectsの複数のセットを削除しようとしている方法は、メモリを大量に消費し(約20MBのライブバイト)、アプリが破棄されています。削除メソッドの実装は次のとおりです。

+ (void)deleteRelatedEntitiesInManagedObjectContext:(NSManagedObjectContext *)context 
{
    NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
    [context setUndoManager:nil];

    [fetch setEntity:[NSEntityDescription entityForName:NSStringFromClass(self) inManagedObjectContext:context]];
    [fetch setIncludesPropertyValues:NO];

    NSError *error = nil;
    NSArray *entities = [context executeFetchRequest:fetch error:&error];

    NSInteger deletedCount = 0;
    for (NSManagedObject *item in entities) {
        [context deleteObject:item];
        deletedCount++;

        if (deletedCount == 500) {
            [context save:&error];
            deletedCount = 0;
        }
    }

    if (deletedCount != 0) {
        [context save:&error];
    }
}

-setFetchBatchSizeを試しましたが、さらに多くのメモリが使用されています。

これを行うためのよりメモリ効率の良い方法は何でしょうか?

4

6 に答える 6

12

編集:2015 WWDCの「コアデータの新機能」(これは常に最初に視聴するビデオですが、今年は非常に忙しいです)を視聴し、新しいAPIを発表しましたNSBatchDeleteRequest。これは以前のソリューションよりもはるかに効率的です。


効率的には複数の意味があり、ほとんどの場合、ある種のトレードオフを意味します。ここでは、削除中にメモリを含めたいだけだと思います。

Core Dataには、単一のSOの質問の範囲を超えて、多くのパフォーマンスオプションがあります。

メモリの管理方法は、managedObjectContextとfetchRequestの設定によって異なります。ドキュメントを見て、すべてのオプションを確認してください。ただし、特に、これらのことを覚えておく必要があります。

また、パフォーマンスの側面にも注意してください。このタイプの操作は、別のスレッドで実行する必要があります。

また、オブジェクトグラフの残りの部分も機能することに注意してください(CoreDataが関連オブジェクトの削除を処理する方法のため)。

メモリ消費に関しては、MOCには特に注意すべき2つの特性があります。ここにはたくさんありますが、決して包括的ではありません。実際に何が起こっているかを確認したい場合は、各保存操作の直前と直後にMOCをNSLogします。特に、registeredObjectsとdeletedObjectsをログに記録します。

  1. MOCには、登録済みオブジェクトのリストがあります。デフォルトでは、登録されたオブジェクトは保持されません。ただし、retainsRegisteredObjectsがYESの場合、登録されているすべてのオブジェクトが保持されます。

  2. 特に削除の場合、setPropagatesDeletesAtEndOfEventは、関連するオブジェクトの処理方法をMOCに指示します。保存で処理する場合は、その値をNOに設定する必要があります。それ以外の場合は、現在のイベントが完了するまで待機します

  3. 非常に大きなオブジェクトセットがある場合は、fetchLimitの使用を検討してください。障害は多くのメモリを消費しませんが、それでもいくらかを消費し、一度に何千も重要ではありません。より多くのフェッチを意味しますが、メモリの量を制限します

  4. また、大きな内部ループがある場合は、独自の自動解放プールを使用する必要があることも考慮してください。

  5. このMOCに親がある場合、保存するとそれらの変更のみが親に移動します。この場合、親MOCがある場合は、それを成長させるだけです。

メモリを制限するために、これを考慮してください(必ずしもあなたのケースに最適ではありません-コアデータオプションがたくさんあります-他の場所で使用しているすべてのオプションに基づいて、あなただけがあなたの状況に最適なものを知っています。

これと非常によく似た、保存がバッキングストアに確実に送られるようにするときに保存に使用するカテゴリをNSManagedObjectContextに作成しました。MOC階層を使用しない場合は必要ありませんが、階層を使用しない理由は実際にはありません(古いiOSにバインドされている場合を除く)。

- (BOOL)cascadeSave:(NSError**)error {
    __block BOOL saveResult = YES;
    if ([self hasChanges]) {            
        saveResult = [self save:error];
    }
    if (saveResult && self.parentContext) {
        [self.parentContext performBlockAndWait:^{
            saveResult = [self.parentContext cascadeSave:error];
        }];
    }
    return saveResult;
}

コードを少し変更しました...

+ (void)deleteRelatedEntitiesInManagedObjectContext:(NSManagedObjectContext *)context 
{
    NSFetchRequest *fetch = [[NSFetchRequest alloc] init];
    [context setUndoManager:nil];

    [fetch setEntity:[NSEntityDescription entityForName:NSStringFromClass(self) inManagedObjectContext:context]];
    [fetch setIncludesPropertyValues:NO];
    [fetch setFetchLimit:500];

    NSError *error = nil;
    NSArray *entities = [context executeFetchRequest:fetch error:&error];
    while ([entities count] > 0) {
        @autoreleasepool {
            for (NSManagedObject *item in entities) {
                [context deleteObject:item];
            }
            if (![context cascadeSave:&error]) {
                // Handle error appropriately
            }
        }
        entities = [context executeFetchRequest:fetch error:&error];
    }
}
于 2012-05-14T12:13:42.803 に答える
1

インスピレーションの瞬間に、私は削除しまし[fetch setIncludesPropertyValues:NO];た、そしてそれは良かったです。ドキュメントから:

通常のフェッチ(includesPropertyValuesはYES)中に、Core Dataは一致するレコードのオブジェクトIDとプロパティデータをフェッチし、行キャッシュに情報を入力して、管理対象オブジェクトを障害として返します(returnsObjectsAsFaultsを参照)。これらの障害は管理対象オブジェクトですが、障害が発生するまで、それらのすべてのプロパティデータは行キャッシュに残ります。障害が発生すると、CoreDataは行キャッシュからデータを取得します。データベースに戻る必要はありません。

割り当てられたライブバイトを約13MBに減らすことができましたが、これはより良い方法です。

于 2012-05-15T06:47:33.527 に答える
1

NSBatchDeleteRequestは私のために働いた; 管理対象オブジェクトの削除時間を5分の1に短縮し、メモリの急増はありませんでした。

于 2016-07-02T05:53:37.513 に答える
0

これは面白いテストになるでしょう。マギアレコードを使ってみてください。そこには非常に効率的であると思われる切り捨て方法があります(私は問題なく3000レコードまでのデータセットでそれを使用しました。それが10,000をどのように処理するかを見るのは興味深いです。

私はそれをその機能だけに使うのではなく、もしあなたがそれを試したことがなければ、そうすべきです。これにより、コアデータの処理が非常に簡単になり、コードが大幅に削減されます。

お役に立てれば。

于 2012-05-14T11:00:54.307 に答える
0

私は決してそれをテストしていませんが、メモリがあなたの主な関心事であるならば、あなたは追加の自動解放プールに500の削除のそれらのバッチをカプセル化することを試みることができます。context:saveは、実行ループサイクルが完了するまで解放されない、かなりの数の自動解放されたオブジェクトを作成する可能性があります。10,000以上のレコードがあると、非常にうまく合計できます。

于 2012-05-14T11:25:09.313 に答える
0

別のAPIを使用したくない場合は、、の別の機能を試してみてNSFetchRequestください。これは、iTunes U iPadコースの1つで、CoreDataを使用した大量の処理を含む例で見たことがあります。fetchLimitfetchOffset

NSInteger limit = 500;
NSInteger count = [context countForFetchRequest:fetch error:nil];
[fetch setFetchLimit:limit];
for (int i=0; i < count/limit+1; i++) {
   // do the fetch and delete and save
}

を調整しfetchLimitて、メモリ要件を満たすことができます。

于 2012-05-14T11:34:32.290 に答える