0

次のコードを使用して、Webサービスからデータをロードし、CoreDataDBに保存します。

    dispatch_queue_t fetchQ = dispatch_queue_create("fetcher", NULL);
dispatch_async(fetchQ, ^{
    NSArray *list = [WebService loadData];
    dispatch_sync(dispatch_get_main_queue(), ^{
        if (list != nil) {
            for (NSDictionary *data in list) {

                NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:appDelegate.coreDataDatabase.managedObjectContext];
                NSFetchRequest *request = [[NSFetchRequest alloc] init];
                [request setEntity:entityDescription];

                NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id = %i", [[data objectForKey:@"id"] integerValue]];
                [request setPredicate:predicate];

                NSError *error = nil;
                NSArray *data = [appDelegate.coreDataDatabase.managedObjectContext executeFetchRequest:request error:&error];

                Object *object = [data objectAtIndex: 0];
                if (object == nil) {
                    object = [NSEntityDescription insertNewObjectForEntityForName:@"Object" inManagedObjectContext:appDelegate.coreDataDatabase.managedObjectContext];
                    object.id = [NSNumber numberWithInteger:[[data objectForKey:@"id"] integerValue]];
                }

                object.name = [data objectForKey:@"name"];
            }

            [appDelegate.coreDataDatabase saveToURL:appDelegate.coreDataDatabase.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil];
        }
    });
});
dispatch_release(fetchQ);

コードは単純化されていますが、シーケンスを表す必要があります。Webサービスへのアクセスはバックグラウンドで実行されます。その後、ストアはメインスレッドで呼び出されます。実際に大丈夫ですか?

残念ながら、全体がすべてのデバイスで機能するわけではありません。私のすべてのデバイスと他のほとんどのデバイスで、問題はありません。いくつかの、しかし明らかにそうです、残念ながら店にはいくつかの否定的なBewertugnenといくつかの質問があるからです。問題は、彼がDBに既存のレコードを見つけ、常に新しい投資をしているわけではないことです。

助けてくれてありがとう、ステファン

4

2 に答える 2

1

私はあなたの実際の質問を理解するのに苦労しています。これが何を意味するのか正確にはわかりません...

いくつかの、しかし明らかにそうです、残念ながら店にはいくつかの否定的なBewertugnenといくつかの質問があるからです。問題は、彼がDBに既存のレコードを見つけ、常に新しい投資をしているわけではないことです。

だから私は最善の推測をします。

コードから、UIManagedDocumentを使用していると思います。

直接保存しないでくださいUIManagedDocument(saveToUrl)。代わりに、自動保存を適切に処理できるようにする必要があります。

NSUndoManagerでアクティブな場合は、UIManagedDocument何もする必要はありません。すべてのアクションが自動的に保存されます。元に戻るマネージャーがない場合は、データがダーティであることを認識させるために、元にできるマネージャーを突く必要があります。あなたはそれをします:

[managedDocument updateChangeCount:UIDocumentChangeDone];

ただし、UIManagedDocumentネストされたコンテキストを使用していることに注意してください。新しいオブジェクトの挿入に関連するバグがいくつかあります。つまり、永続IDは子コンテキストに適切にプッシュバックされません。あなたの問題についての私の仮定は、これが原因で重複したデータエントリを取得しているということです。

このSOの質問は、この問題にある程度深く取り組んでいます。

getPermanantIDsの後、コアデータはオブジェクトの障害を完全に満たすことができませんでした

基本的に、を続行することを考慮して、いくつかの選択肢がありますUIManagedDocument。まず、永続的なIDを取得します。この行を置き換えます:

[appDelegate.coreDataDatabase saveToURL:appDelegate.coreDataDatabase.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:nil];

これで(もちろん-エラーを処理します-これは0を渡します):

NSManagedObjectContext *moc = appDelegate.coreDataDatabase.managedObjectContext;
[moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:0];
[appDelegate.coreDataDatabase updateChangeCount:UIDocumentChangeDone];

これにはまだいくつかの問題がありますが、それらは非常にまれです。コードには非常に複雑なオブジェクトグラフが含まれていないように見えるため、これで十分です。

もう1つのオプションは、別のMOC(UIManagedDocument親コンテキストの子)にインポートし、インポートが完了したときにメインコンテキストを再フェッチすることです。

コード例を使用すると、これを行う簡単な方法は次のようになります...

dispatch_queue_t fetchQ = dispatch_queue_create("fetcher", NULL);
dispatch_async(fetchQ, ^{
    NSArray *list = [WebService loadData];
    NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
    moc.parentContext = appDelegate.coreDataDatabase.managedObjectContext.parentContext;
    for (NSDictionary *data in list) {
        NSEntityDescription *entityDescription = [NSEntityDescription entityForName:@"Object" inManagedObjectContext:moc];
        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        [request setEntity:entityDescription];

        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id = %i", [[data objectForKey:@"id"] integerValue]];
        [request setPredicate:predicate];

        NSError *error = nil;
        NSArray *data = [moc executeFetchRequest:request error:&error];
        Object *object = [data objectAtIndex: 0];
        if (object == nil) {
            object = [NSEntityDescription insertNewObjectForEntityForName:@"Object" inManagedObjectContext:moc];
            object.id = [NSNumber numberWithInteger:[[data objectForKey:@"id"] integerValue]];
        }

        object.name = [data objectForKey:@"name"];
    }

    // Save the MOC you just loaded everything into
    [moc save:&error]; // handle error...
    // Save the parent MOC, which is also the parent of your main MOC
    moc = moc.parentContext;
    [moc performBlock:^{
        [moc save:&error]; // handle error
        // When all the saves are done, run on the main queue and just refetch
        // all the entities that were inserted.
        dispatch_async(dispatch_get_main_queue(), ^{
            // Here, use appDelegate.coreDataDatabase.managedObjectContext
            // to refetch the objects that were altered.  If you are using a
            // table view or FRC just issue a refetch of the data and the
            // main MOC will get everything that was modified.
        });
    }];
});
dispatch_release(fetchQ);
于 2012-08-25T15:44:11.557 に答える
0

メインスレッドでコンテキストを作成した場合、そのコンテキストへのすべてのアクセスもメインスレッド上にある必要があります(iOS5より前の構成を想定-コアデータはiOS5以降の3つのモードをサポートします)。

シリアルディスパッチキューを作成し、そこにmocを作成して、そのキューを介してすべてのアクセスをファネルすることができます。ディスパッチキューはアクセスをシリアル化するため、同時実行は発生しません。

私自身、iOS5のプライベートキュー機能を使用するように変換しました。この場合、すべてのアクセスは「[moc Performblock ...]」によって行われます。変換に1時間もかからず、コードは実際にクリーンになりました(I前述のようにシリアルキューを使用していました)。

于 2012-08-25T12:38:39.447 に答える