2012-08-24 3 views
10

これまでに質問されていますが、アプリのニーズに十分に速いソリューションは記載されていません。コアデータ:エンティティタイプのすべてのオブジェクトを削除します。つまり、テーブルをクリアします。

私たちが設定した通信プロトコルでは、サーバーは同期が実行されるたびにすべての顧客の新しいセットを送ります。以前は、plistとして保存していました。今、コアデータを使用したい。

何千ものエントリがあります。それぞれを個別に削除するには長い時間がかかります。コアデータ内の特定のテーブルのすべての行を削除する方法はありますか?

delete from customer 

この呼び出しは即座に発生します。 Core Dataで個別に1つずつ実行すると、iPad1で30秒かかることがあります。

コアデータをシャットダウンする、つまり永続ストアとすべての管理対象オブジェクトのコンテキストを削除してsqliteにドロップし、テーブルに対してdeleteコマンドを実行するのは妥当ですか?このプロセス中に他のアクティビティは実行されていないので、データベースの他の部分にアクセスする必要はありません。

+0

代わりに、通信プロトコルの動作方法を変更することもできます。あなたの顧客リストへの変更をあなたのデバイスに送るだけを検討してください。詳細については、http://stackoverflow.com/questions/5035132/how-to-sync-iphone-core-data-with-web-server-and-then-push-to-other-devices/5052208#5052208を参照してください。どのようにこれを行うことができます。 – chris

+0

http://stackoverflow.com/a/5733105/2412290このサイトは良いアイデアです、使用関係上位エンティティを削除サブ – mosn

答えて

25

デイブ・デロングはすべてについてちょうど、まあ、専門家であり、私は水の上を歩く方法をイエスに言っているようなので、私は感じています。確かに、彼の投稿は2009年からのもので、これは長い時間前です。

しかし、Botによって投稿されたリンクのアプローチは、必ずしも大きな削除を処理する最良の方法ではありません。

基本的に、この投稿は、オブジェクトIDをフェッチし、各オブジェクトでdeleteを呼び出すことを提案しています。

問題は、1つのオブジェクトを削除すると、関連するすべての関係を処理する必要があり、それがさらにフェッチを引き起こす可能性があるということです。

このように大規模な削除を行う必要がある場合は、特定のコアデータストアのテーブルを分離できるように全体のデータベースを調整することをお勧めします。そうすれば、ストア全体を削除し、残したい小さなビットを再構築することができます。おそらく最も速いアプローチだろう。あなたがオブジェクト自体を削除する場合

しかし、あなたはこのパターンに従ってください...

は、自動解放プール内で、バッチであなたの削除を行い、そして任意のカスケード接続関係をプリフェッチしてください。これらはすべて、実際にデータベースにアクセスしなければならない回数が最小限に抑えられ、削除の実行に要する時間が短縮されます。ダウンに来て提案のアプローチでは、

...

  1. がリストを
  2. 反復を削除するすべてのオブジェクトのたObjectIdを取得し、あなたの場合は、各オブジェクトに

を削除カスケード関係を持っていると、データベースへの余分なトリップが発生し、IOは非常に遅いです。データベースにアクセスする回数を最小限に抑えたいとします。

最初は不自然なように聞こえるかもしれませんが、削除したいと考えるよりも多くのデータを取り込む必要があります。その理由は、いくつかのIO操作ですべてのデータをデータベースからフェッチできるからです。

だから、あなたのフェッチ要求に、設定したい...それらの関係は、カスケードルールを削除した可能性のあるすべての関係を表す

[fetchRequest setRelationshipKeyPathsForPrefetching:@[@"relationship1", @"relationship2", .... , @"relationship3"]]; 

フェッチが完了すると、削除されるすべてのオブジェクトと、削除されるオブジェクトの結果として削除されるオブジェクトがあります。

階層が複雑な場合は、できるだけ先にプリフェッチする必要があります。それ以外の場合は、オブジェクトを削除すると、Core Dataはカスケード削除を管理できるように、オブジェクトごとに個々の関係を個別にフェッチする必要があります。

これにより、結果としてより多くのIO操作が行われるため、時間がかかります。

フェッチが完了したら、オブジェクトをループして削除します。大規模な削除の場合、大規模な処理のスピードアップを見ることができます。

さらに、オブジェクトがたくさんある場合は、複数のバッチに分割し、自動解放プール内で実行します。

最後に、別のバックグラウンドスレッドでこれを行います。これにより、UIは保留されません。永続的なストアコーディネータに接続された別個のMOCを使用して、主MOCにDidSave通知を処理させて、そのコンテキストからオブジェクトを削除させることができます。これはコードのように見えますが、擬似コードとして扱い

...もちろん

NSManagedObjectContext *deleteContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateConcurrencyType]; 
// Get a new PSC for the same store 
deleteContext.persistentStoreCoordinator = getInstanceOfPersistentStoreCoordinator(); 

// Each call to performBlock executes in its own autoreleasepool, so we don't 
// need to explicitly use one if each chunk is done in a separate performBlock 
__block void (^block)(void) = ^{ 
    NSFetchRequest *fetchRequest = // 
    // Only fetch the number of objects to delete this iteration 
    fetchRequest.fetchLimit = NUM_ENTITIES_TO_DELETE_AT_ONCE; 
    // Prefetch all the relationships 
    fetchRequest.relationshipKeyPathsForPrefetching = prefetchRelationships; 
    // Don't need all the properties 
    fetchRequest.includesPropertyValues = NO; 
    NSArray *results = [deleteContext executeFetchRequest:fetchRequest error:&error]; 
    if (results.count == 0) { 
     // Didn't get any objects for this fetch 
     if (nil == results) { 
      // Handle error 
     } 
     return; 
    } 
    for (MyEntity *entity in results) { 
     [deleteContext deleteObject:entity]; 
    } 
    [deleteContext save:&error]; 
    [deleteContext reset]; 

    // Keep deleting objects until they are all gone 
    [deleteContext performBlock:block]; 
}; 

[deleteContext preformBlock:block]; 

は、あなたが適切なエラー処理を行う必要があるが、それは基本的な考え方です。

削除するデータがあまりにも多い場合は、バッチでフェッチしてください。これにより、メモリが不自由になることがあります。 すべてのプロパティを取得しないでください。 IO操作を最小限に抑えるために関係をプリフェッチします。 メモリを増やさないようにするには、autoreleasepoolを使用してください。 コンテキストを削除します。 タスクをバックグラウンドスレッドで実行します。

本当に複雑なグラフがある場合は、オブジェクトグラフ全体のすべてのエンティティのカスケード関係をすべてプリフェッチしてください。

注意してください。メインコンテキストでは、DeleteSave通知を処理して、削除に合わせてコンテキストを保持する必要があります。

EDIT

感謝。たくさんの良い点。なぜなら、なぜ 別のMOCを作成するのですか?任意のテーブルからすべての行を削除するsqliteを使用して データベース全体を削除しない任意の考えですか? - David

長い削除操作が行われている間はUIがブロックされないように、別のMOCを使用します。データベースへの実際のコミットが発生すると、データベースにアクセスできるスレッドは1つだけなので、他のアクセス(フェッチなど)によって更新が遅れることに注意してください。これは、大きな削除操作をチャンクに分割する別の理由です。小さな作品は、操作全体が完了するまで待つことなく、他のMOCが店にアクセスする機会を提供します。

これが原因で問題が発生した場合は、dispatch_set_target_queueを介して優先キューを実装することもできますが、これはこの質問の範囲を超えています。

コアデータデータベースでsqliteコマンドを使用する場合、Appleは繰り返しこれは悪い考えであり、Core Dataデータベースファイルに対して直接SQLコマンドを実行しないでください。


最後に、これに注意してください。私の経験では、重大なパフォーマンス上の問題が発生した場合、通常は設計が不良であるか、実装が不適切であることが判明しています。問題をもう一度見直し、このユースケースに合わせてシステムをいくらか再設計できるかどうかを確認してください。

すべてのデータを送信する必要がある場合は、バックグラウンドスレッドでデータベースを照会し、新しいデータをフィルタリングして、データを3つのセットに分割する必要があります。変更が必要なオブジェクト、削除が必要なオブジェクト、挿入される。

このようにして、変更が必要なデータベースのみを変更します。

毎回ほとんど新しいデータがある場合は、これらのエンティティが独自のデータベースを持つデータベースを再構築することを検討してください(データベースにはすでに複数のエンティティが含まれていることが前提です)。そうすれば、ファイルを削除して新しいデータベースでやり直すことができます。それは速いです。今、数千のオブジェクトを再挿入するのは速くないでしょう。

店舗を問わず、手動で関係を管理する必要があります。それは難しいことではありませんが、同じ店舗内の関係のように自動ではありません。

これを実行した場合は、まず新しいデータベースを作成し、既存のデータベースを破棄して新しいデータベースと置き換え、古いデータベースを削除します。

このバッチメカニズムを使用してデータベースを操作するだけで、オブジェクトグラフの管理が不要な場合は、コアデータの代わりにsqliteを使用することを検討してください。

+0

ありがとうございます。たくさんの良い点。以外はすべて説明が分かります。なぜ、別のMOCを作成するのですか?任意のテーブルからすべての行を削除するsqliteを使用して、データベース全体を削除しない任意の考えですか? – David

+0

いい回答です。エンティティに「独自のデータベース」を持たせる方法は、それらのエンティティに別々のNSPersistentStoreを使用し、ディスクからそのストアだけを削除することです。 NSPersistentStoreの「構成」だけで、特定のタイプのエンティティが確実にその特定のストアに入るようにすることができます。 –

+0

@JesseRusak素晴らしい点。あなたが複数の店舗を持つことができるということを知らない人もいます。 –

-1

はい、永続ストアを削除してゼロから開始するのは妥当です。これはかなり速く起こります。できることは、永続ストアコーディネータからの永続ストア(永続ストアURL付き)を削除してから、永続ストアのURLを使用してディレクトリフォルダからデータベースファイルを削除することです。私はNSFileManagerのremoveItemAtURLを使ってそれを行いました。

編集:考慮すべき点:現在のNSManagedObjectContextインスタンスを無効/解除し、同じ永続ストアを使用しているNSManagedObjectContextで何かをしている他のスレッドを停止するようにしてください。コンテキストが永続ストアにアクセスしようとすると、アプリケーションがクラッシュします。

1

本当にあなたの唯一の選択肢は、それらを個別に削除することです。私はオブジェクトのトンでこのメソッドを実行し、それはかなり高速です。ここでは、管理対象オブジェクトIDをロードするだけで、誰かがそれを実行し、不要なオーバーヘッドを防ぎ高速化する方法があります。

Core Data: Quickest way to delete all instances of an entity

6

のiOS 9以降

使用NSBatchDeleteRequest。私は400,000を超えるインスタンスを持つCore Dataエンティティのシミュレータでこれをテストし、削除はほぼ瞬時に行われました。the answerにリンクされていると述べた@JodyHaginsもこの方法に更新されていること@Botこと

// fetch all items in entity and request to delete them 
let fetchRequest = NSFetchRequest(entityName: "MyEntity") 
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) 

// delegate objects 
let myManagedObjectContext = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext 
let myPersistentStoreCoordinator = (UIApplication.sharedApplication().delegate as! AppDelegate).persistentStoreCoordinator 

// perform the delete 
do { 
    try myPersistentStoreCoordinator.executeRequest(deleteRequest, withContext: myManagedObjectContext) 
} catch let error as NSError { 
    print(error) 
} 

は注意してください。

関連する問題