ios - 在另一个上下文中发生更改后,如何正确地从 NSFetchedResultsController 执行Fetch?

标签 ios objective-c core-data refresh fetch

当我在另一个具有自己的上下文的顶层 Controller 中进行一些更改时,我会向 UITableViewController 类发布更新单元格的通知。此通知在将 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 委托(delegate)方法:

- (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];

核心数据 Controller :

- (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 协议(protocol)方法来动态更新您的 TableView 单元格。 https://developer.apple.com/library/prerelease/ios/documentation/CoreData/Reference/NSFetchedResultsControllerDelegate_Protocol/index.html

如果您使用不同的托管对象上下文进行更新,则在这些更改反射(reflect)到您的上下文中之前,您还必须合并这些更改。

更新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];
  }

关于ios - 在另一个上下文中发生更改后,如何正确地从 NSFetchedResultsController 执行Fetch?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28141449/

相关文章:

ios - os.log 是做什么用的?

ios - 如何使用 Core Graphics 在 `drawRect` 中填充一个 unicode 符号?

ios - ios中uiimageView的字节数组

iphone - 如何在圆圈内对齐 UIButtons?

cocoa - NSManagedObject 子类和 setValuesForKeysWithDictionary :

iphone - iPad 上的 Flash cs5 动画

iphone - 将 UIAlertView 添加到 UIScrollView

ios - 为 NSFetchedResultsController 创建关系谓词的正确方法

ios - 父/子和一对一/多逆向关系之间的核心数据差异

swift - 无法将类型 '[Message]' 的值转换为预期的参数类型 'Message'