0

UIManagedDocumentバックグラウンド スレッドを使用して/内のデータを更新する際に問題が発生していますCore Data。具体的にはNSFetchResultsController、バックグラウンド スレッドからジオコーディングされたデータに基づいてマップ アノテーションを更新するために を使用していますが (メインの MOC にマージして戻した後)、マップは更新されませんUIManagedDocument。 (親子)。アプリを閉じて再度開くと、注釈が入力されるため、ある時点で永続ストアへのコミットが発生しますが、そのようなコミットを強制してNSFetchResultsController. ここにいくつかのコードがあります:

MOC を更新するバックグラウンド スレッド:

- (void) populateGPSCoordsInClubsInContext: (NSManagedObjectContext *) mainCtx
{            
    dispatch_queue_t MapFetchQ = dispatch_queue_create("Google Map Data Fetcher", NULL);
    dispatch_async(MapFetchQ, ^{

        NSManagedObjectContext * ctxThread = [[NSManagedObjectContext alloc] init];
        [ctxThread setPersistentStoreCoordinator:mainCtx.persistentStoreCoordinator];


        NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Club"];
        request.predicate = [NSPredicate predicateWithFormat:@"inRegion.name=%@", self.name];
        NSError *error = nil;

        NSArray * clubs = [ctxThread executeFetchRequest:request error:&error];

        NSLog(@"[%@] Fetching map data. Club count is %d", self.name, [clubs count]);  

        int delayCounter = 0;

        for(Club * club in clubs)
        {
            if(![club.hasCoord boolValue] && club != nil)
            {
                delayCounter++; // to deal with google maps api's DoS protection            

                [club setLongitudeAndLattitudeFromGoogle];
                NSError * error;

                if(![ctx save:&error])
                 NSLog(@"[%@] Problem saving region to database.", self.name);

            }

            if(delayCounter == 8)
            {            
                [NSThread sleepForTimeInterval:(NSTimeInterval)2.0];
                delayCounter = 0;
            }
        }
    });
    dispatch_release(MapFetchQ);
}

これらの保存が呼び出されると、次のようにメイン スレッド (アプリ デリゲート内) で通知を取得します。

- (void) contextDidSave: (NSNotification *) notification
{
    NSManagedObjectContext * ctx = [self.clubsDB managedObjectContext];
    [ctx mergeChangesFromContextDidSaveNotification:notification];

    NSArray * updates = [[notification.object updatedObjects] allObjects];

    for(Club * club in updates) // This never fires because updates never has objects
    {
        NSLog(@"*********** %@", club.name);
    }

    NSLog(@"[%@] %@", [self class], NSStringFromSelector(_cmd));

}

そして、フェッチした結果コントローラーを次のように設定しました (述語は正しいです。データがストアにコミットされた後にアプリを再起動すると、結果は期待どおりになります)。

-(void) setupFRC
{

    NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Club"];
    request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]];
    request.predicate = [NSPredicate predicateWithFormat:@"inRegion.name=%@ AND hasCoord=%@",[self.clubsDB regionTitleAsString], [NSNumber numberWithBool:YES]]; // Follow the relationshop and only display clubs from THIS region.

    //request.predicate = [NSPredicate predicateWithFormat:@"inRegion.name=%@",[self.clubsDB regionTitleAsString]];

    self.debug = YES;

    self.fetchedResultsController = 
    [[NSFetchedResultsController alloc] initWithFetchRequest:request 
                                        managedObjectContext:self.clubsDB.managedObjectContext
                                          sectionNameKeyPath:nil
                                                   cacheName:nil];
}

適切なMOCを更新して、フェッチされた結果コントローラーを希望どおりに動作させる方法についてのアイデアはありますか?

4

3 に答える 3

0

OK、コードを編集して、どのように表示されるかを示します。UIManagedDocumentからpopulateGPSCoordsInClubsContextにMOCを渡していると仮定します。すでに行っていることにはほとんど違いがないことに注意してください。しかし、私たちが知っているように、1行のコードですべての違いを生むことができます...

// NOTE: Make it clear you expect to work on a document...
- (void) populateGPSCoordsInClubsInContext: (UIManagedDocument *) document
{            
    dispatch_queue_t MapFetchQ = dispatch_queue_create("Google Map Data Fetcher", NULL);
    dispatch_async(MapFetchQ, ^{

        NSManagedObjectContext * ctxThread = [[NSManagedObjectContext alloc] init];
        // NOTE: Make changes up into the context of the document
        ctxThread.parentContext = document.managedObjectContext;    

        NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"Club"];
        request.predicate = [NSPredicate predicateWithFormat:@"inRegion.name=%@", self.name];
        NSError *error = nil;

        NSArray * clubs = [ctxThread executeFetchRequest:request error:&error];

        NSLog(@"[%@] Fetching map data. Club count is %d", self.name, [clubs count]);  

        int delayCounter = 0;

        for(Club * club in clubs)
        {
            if(![club.hasCoord boolValue] && club != nil)
            {
                delayCounter++; // to deal with google maps api's DoS protection            

                [club setLongitudeAndLattitudeFromGoogle];
                NSError * error;

                // NOTE: This notifies the parent context of the changes.
                if(![ctx save:&error])
                 NSLog(@"[%@] Problem saving region to database.", self.name);
                // NOTE: However, since a UIManagedDocument is an "auto-save"
                // document, we need to tell it that is is dirty...
                [document updateChangeCount:UIDocumentChangeDone];
            }

            if(delayCounter == 8)
            {            
                [NSThread sleepForTimeInterval:(NSTimeInterval)2.0];
                delayCounter = 0;
            }
        }
    });
    dispatch_release(MapFetchQ);
}

この方法でそれを行うことのクールな点の1つは、通知を処理する必要さえないことです(少なくとも一貫性のためではありません)。

あなたがそれを他の方法でやりたいのなら、あなたはこれをすることができます...

        ctxThread.parentContext = document.parentContext;    

その場合、ドキュメントでupdateChangeCountを呼び出さないでください。これらの変更は、親コンテキストとファイルに反映されます。ただし、これを行うと、将来のフェッチで通知が自動的に表示されるため、通知を処理する必要もありません。もちろん、変更を更新したい場合でも、通知を処理することはできますが、それだけです。フェッチ時にそれらを表示するには、他に何もする必要はありません。

一般に信じられていることとは反対に、UIManagedDocumentは非常に効果的で簡単です(ルールを知っていれば)。

于 2012-04-17T22:23:15.630 に答える
0

私は実際に問題を解決することができました。秘訣は、FRCを設定する前に、編集中のオブジェクトがコンテキストに事前入力されていることを確認することでした。率直に言って、それは非常に難解であり、UIManagedDocumentが期待どおりに機能しないという事実(またはドキュメントで説明されているように)は当惑させられます。

于 2012-04-03T14:31:55.223 に答える
0

UIManagedDocument がコミットを送信する方法にも問題があります。私が考えることができる唯一の解決策は、UIManagedDocument の使用を停止し、デフォルトのマスター/ディテール テンプレートで提供される PersistentStore のコンテキストを使用することです。

編集: さらに調査した結果、UIManagedDocument が変更をコミットする方法がないように思われるため、永続ストアから作成されたコンテキストを渡す方がよいでしょう。Apple が UIManagedDocument の使用可能なサンプル コードをまだ提供していないのは偶然ではないようです。デフォルトのテンプレートをそのまま使用します。

同様の問題に直面している誰かへのリンクとその「解決策」を次に示します。

Core Data 管理対象オブジェクトは、シミュレータを再起動するまで関連オブジェクトを認識しません

于 2012-04-02T09:54:27.553 に答える