数据导入后,FetchedResultsController没有看到managedObjectContext中的更改

时间:2012-07-18 07:33:40

标签: objective-c ios core-data nsfetchedresultscontroller nsmanagedobjectcontext

我正在开发我的应用程序中的数据导入部分,为了使UI更可靠,我按照这篇Marcus Zarra的文章 http://www.cimgf.com/2011/08/22/importing-and-displaying-large-data-sets-in-core-data/

我的想法是你在后台的单独上下文中导入(我使用GCD),你的fetchedResultsController的上下文通过观察 NSManagedObjectContextDidSaveNotification 来合并更改。

我得到的问题对我来说很奇怪 - 我的fetchedResultsController没有获得那些更改,并且在新数据到来时不会重新加载TableView。 但是,如果我触发以下方法,这将使得获取并重新加载表 - 它会在那里得到它。

- (void)updateUI
{                                         
  NSError *error;
  if (![[self fetchedResultsController] performFetch:&error]) {
    NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
  }
  [self.tableView reloadData];
}

所以现在我调用那个方法,当我得到 NSManagedObjectContextDidSaveNotification 让它工作时,但它看起来很奇怪而且讨厌我。

 - (void)contextChanged:(NSNotification*)notification
{
  if ([notification object] == [self managedObjectContext]) return;

  if (![NSThread isMainThread]) {
    [self performSelectorOnMainThread:@selector(contextChanged:) withObject:notification waitUntilDone:NO];
    return;
  }

  [[self managedObjectContext] mergeChangesFromContextDidSaveNotification:notification];
  //TODO:Make it work as it should - merge, without updateUI
   [self updateUI];//!!!Want to get rid of this!
}

为什么会这样? 以下是负责解析数据并添加Observer的代码。

- (void)parseWordsFromServer:(NSNotification *)notification
{

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) , ^{
    NSDictionary *listInJSON = [notification userInfo];
    wordsNumbers = [[listInJSON valueForKey:@"words"]mutableCopy];

    if ([wordsNumbers count])
    {
      [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];

//新线程的新上下文            NSManagedObjectContext * backContext = [[AppDelegate sharedAppDelegate] backManagedObjectContext];

      //Get all the words we already have on this device
      NSArray *wordsWeHave = [Word wordsWithNumbers:wordsNumbers inManagedContext:backContext];
      //Add them to this list
      for (Word *word in wordsWeHave)
        [[List listWithID:[currentList listID] inManagedObjectContext:backContext]addWordsObject:word];
      [backContext save:nil];!//Save the context - get the notification
         }

  });

}

修改 我使用NSFetchedResutsControllerDelegate,实际上,如果我不这样做,我怎么能假装我的tableview更新?

更新决定转移到家长 - 儿童范例

1 个答案:

答案 0 :(得分:6)

这个问题已经多次讨论过了,比如NSFetchedResultsController doesn't show updates from a different context,而且很难理解发生了什么,但是我的笔记很少。

首先,您违反了一条简单的规则:您需要为每个线程(Concurrency with Core Data Section)设置一个托管对象上下文。

  

为每个线程创建一个单独的托管对象上下文并共享一个   单一持久性商店协调员。

因此,在自定义线程内部访问主上下文,获取其持久协调器并将其设置为新上下文。

NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
[moc setPersistentStoreCoordinator:persistentStoreCoordinatorGrabbedFromAppDelegate];

其次,您无需注册

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contextChanged:) name:NSManagedObjectContextDidSaveNotification object:nil];

在新线程中。只需在创建新线程的类中(或在app委托中)注册它。

最后,如果您没有使用NSFetchedResutsControllerDelegate,请使用它。它允许摆脱重新加载数据表。当上下文发生变化时,委托会响应更改:编辑,删除,添加。

从iOS 5开始,您可以使用新的Core Data API,并使用新的confinement mechanism让您的生活更轻松。

修改

来自 @mros 评论。

  

Multi-Context CoreData

     

它可以帮助您更多地了解它的优点   使用父子核心数据模型。我特别喜欢这一点   关于使用私有队列上下文来处理持久性存储。   请务必阅读整篇文章,因为一开始   显示了如何不这样做。

希望有所帮助。