1

CoreData関連するテーブルがありますNSFetchedResultsController。コントローラには、メイン キューに作成され、読み取り専用で動作するコンテキストがあります。もちろん、プロトコルをtableviewcontroller実装します。NSFetchedResultsControllerDelegate

メソッドの実装を見てみましょう:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
    switch (type) {
        case NSFetchedResultsChangeInsert:
            NSLog(@"Inserted in %@", [NSString stringWithUTF8String:dispatch_queue_get_label(dispatch_get_current_queue())]);
            [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeDelete:
            [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeUpdate:
            NSLog(@"Updated in %@", [NSString stringWithUTF8String:dispatch_queue_get_label(dispatch_get_current_queue())]);
            [_tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

        case NSFetchedResultsChangeMove:
            [_tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
            [_tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;
    }
}

バックグラウンドで、データをダウンロードし、アプリケーションでデータベースを更新します。データベースの更新は常に同じです。私のデータマネージャーの方法で更新されます:

- (void)saveDataInBackgroundInForeignContext:(void (^)(NSManagedObjectContext *))saveBlock completion:(void (^)(void))completion {
    AppDelegate *delegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    dispatch_async([delegate.dispatcher queueForDataSavingInModel:self.modelName], ^{
        [self saveDataInForeignContext:saveBlock];

        dispatch_sync(dispatch_get_main_queue(), ^{
            completion();
        });
    });
}


- (void)saveDataInForeignContext:(void (^)(NSManagedObjectContext *))saveBlock {
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {

        NSManagedObjectContext *localContext = [[NSManagedObjectContext alloc] init];
        [localContext setPersistentStoreCoordinator:coordinator];

        [localContext setMergePolicy:NSMergeByPropertyObjectTrumpMergePolicy];
        [self.managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
        [[NSNotificationCenter defaultCenter] addObserver:self.managedObjectContext
                                                 selector:@selector(mergeChangesFromContextDidSaveNotification:)
                                                     name:NSManagedObjectContextDidSaveNotification
                                                   object:localContext];

        saveBlock(localContext);

        if (localContext.hasChanges) {

            [self updateLastUpdateDateInConformedUpdatedObjects:localContext];

            NSError *error = nil;
            BOOL success = [localContext save:&error];
            if (!success) {
                NSLog(@"Saving in foreign context failed. %@", error.userInfo);
            }
        }

        [localContext release];
    }
}

コンテキストをsaveBlock変更するのは、サーバーからのデータに依存します。したがって、結果では奇妙な動作があります。

最初のリストのNSLoginメソッドに注意してください。controller:didChangeObject:atIndexPathログを見てみましょう。

2012-11-16 02:59:33.376 [27824:5303] Inserted in ru.idecide.saving.calls // WTF WHY?!
2012-11-16 03:05:56.219 [27824:c07] Updated in com.apple.main-thread

ru.idecide.saving.calls - queueデータの保存について。

これは実際には問題ではありません。すべてが機能しますが、メソッドinsertRowsAtIndexPathsは、挿入時に呼び出してから 2 ~ 3 秒で UI に影響を与え、(明らかに) 更新時にすぐに影響を与えます。なぜそれが起こるのですか?それを避けるために私は何ができますか?

4

1 に答える 1

1

問題はここにあります:

    [[NSNotificationCenter defaultCenter] addObserver:self.managedObjectContext
                                             selector:@selector(mergeChangesFromContextDidSaveNotification:)
                                                 name:NSManagedObjectContextDidSaveNotification
                                               object:localContext];

メインスレッド コンテキストをバックグラウンド キュー コンテキストに直接リンクしています。バックグラウンド スレッドで実行されている が通知を投稿するlocalContextと、通知はself.managedObjectContext同じキュー (バックグラウンド キュー) のオブザーバー ( ) に配信されます。

に配信する前に、通知をメイン スレッドに転送する必要がありますself.managedObjectContext。バックグラウンド キューで通知を受け取り、それをメイン スレッドに転送する新しいメソッドを用意します。

- (void)backgroundContextDidSave:(NSNotification *)note {
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.managedObjectContext mergeChangesFromContextDidSaveNotification:note];
    });
}

次に、通知を登録するときにそのメソッド セレクターを使用します。

    [[NSNotificationCenter defaultCenter] addObserver:self.managedObjectContext
                                             selector:@selector(backgroundContextDidSave:)
                                                 name:NSManagedObjectContextDidSaveNotification
                                               object:localContext];
于 2012-11-16T06:19:18.800 に答える