如何在另一个上下文更改后从 NSFetchedResultsController 中适当地执行 Fetch?

How to appropriately performFetch from NSFetchedResultsController after changes in another context?

当我在另一个具有自己的上下文的顶层控制器中进行一些更改时,我 post 通知 UITableViewController class 更新单元格。此通知在将 fetchedResultsController 设置为 nil 后调用 performFetch。但只有当我完全重新加载 UITableViewController 时,我才会看到这种变化。我没有使用缓存。 如何在更新我的 PersistentStore 后立即刷新 tableView 单元格?

- (void)performFetch {
    NSError *error = nil;

    _fetchedResultsController = nil;
    if (![self.fetchedResultsController performFetch:&error]) {
    NSLog(@"Unable to perform fetch. Reason: %@", [error localizedDescription]);
    }
    [self.tableView reloadData];
}


- (NSFetchedResultsController *)fetchedResultsController {
    if (_fetchedResultsController) {
        return _fetchedResultsController;
    } else {
        NSFetchRequest *request = [[NSFetchRequest alloc] init];
        NSEntityDescription *entity = [NSEntityDescription entityForName:@"Item" inManagedObjectContext:self.managedObjectContext];
        NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"orderingValue" ascending:YES];
        [request setSortDescriptors:@[sort]];
        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"syncStatus != %d", ObjectDeleted];
        [request setEntity:entity];
        [request setFetchBatchSize:20];
        [request setPredicate:predicate];

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

    return _fetchedResultsController;
}

更新: 这是 FetchedResultsController 委托方法:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {

    UITableView *tableView = self.tableView;

    switch(type) {

        case NSFetchedResultsChangeInsert:
            [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
            break;

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

        case NSFetchedResultsChangeUpdate:
            [self configureCell:(ItemCell *)[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
            break;

        case NSFetchedResultsChangeMove:
            break;
    }
}

UITableViewController 的上下文:

self.managedObjectContext = [[CoreDataController sharedInstance] newManagedObjectContext];

DetailsViewController 上下文:

self.managedObjectContext = [[CoreDataController sharedInstance] masterManagedObjectContext];

核心数据控制器:

- (NSManagedObjectContext *)masterManagedObjectContext {
    if (_masterManagedObjectContext != nil) {
        return _masterManagedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _masterManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [_masterManagedObjectContext performBlockAndWait:^{
            [_masterManagedObjectContext setPersistentStoreCoordinator:coordinator];
        }];

    }
    return _masterManagedObjectContext;
}

- (NSManagedObjectContext *)newManagedObjectContext {
    NSManagedObjectContext *newContext = nil;
    NSManagedObjectContext *masterContext = [self masterManagedObjectContext];
    if (masterContext != nil) {
        newContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [newContext performBlockAndWait:^{
            [newContext setParentContext:masterContext]; 
        }];
    }

    return newContext;
}

我不确定您的完整设置涉及什么,但您确实需要实施 NSFetchedResultsControllerDelegate 协议方法来动态更新您的 tableview 单元格。 https://developer.apple.com/library/prerelease/ios/documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/index.html

如果您使用不同的托管对象上下文进行更新,则必须先合并这些更改,然后它们才能反映在您的上下文中。

更新1

[[NSNotificationCenter defaultCenter]
        addObserverForName:NSManagedObjectContextDidSaveNotification
                    object:nil
                     queue:nil
                usingBlock:^(NSNotification* note) {
    NSManagedObjectContext *moc = self.managedObjectContext;
    if (note.object != moc) {
        [moc performBlock:^(){
            [moc mergeChangesFromContextDidSaveNotification:note];
        }];
    }
 }];

更新2

您可以为 NSFetchedResultsControllerDelegate 使用以下模板

#pragma mark - NSFetchedResultsControllerDelegate
 - (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView beginUpdates];
}

 - (void)controller:(NSFetchedResultsController *)controller
  didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
       atIndex:(NSUInteger)sectionIndex
     forChangeType:(NSFetchedResultsChangeType)type
{
    switch(type)
   {
    case NSFetchedResultsChangeInsert:
              [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeDelete:
        [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
        break;

    default:
        break;
      }
  }


  - (void)controller:(NSFetchedResultsController *)controller
     didChangeObject:(id)anObject
         atIndexPath:(NSIndexPath *)indexPath
       forChangeType:(NSFetchedResultsChangeType)type
        newIndexPath:(NSIndexPath *)newIndexPath
  {
      switch(type)
      {
          case NSFetchedResultsChangeInsert:
              [self.tableView insertRowsAtIndexPaths:[NSArray       arrayWithObject:newIndexPath]       withRowAnimation:UITableViewRowAnimationFade];
              break;

          case NSFetchedResultsChangeDelete:
              [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
              break;

          case NSFetchedResultsChangeUpdate:
              [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
              break;

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

  - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
  {
      [self.tableView endUpdates];
  }