7

我有一个或多或少的基本UITableViewControllerNSFetchedResultsControllerUITableViewController被推入navigationController's堆栈。但是,推动画并不流畅,因为NSFetchedResultsController的提取是在主线程上执行的,因此会阻止用户界面。NSFetchedResultsController:在后台线程中获取

我的问题是:如何在后台线程中执行NSFetchedResultsController的提取以保持动画顺畅?

NSFetchedResultsController和委托方法是这样的:

- (NSFetchedResultsController *)fetchedResultsController 
{ 
    if (_fetchedResultsController != nil) { 
     return _fetchedResultsController; 
    } 

    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
    // Edit the entity name as appropriate. 
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"GPGrade" inManagedObjectContext:self.managedObjectContext]; 
    [fetchRequest setEntity:entity]; 

    // Set the batch size to a suitable number. 
    [fetchRequest setFetchBatchSize:20]; 

    //Set predicate 
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"parent == %@", self.subject]; 
    [fetchRequest setPredicate:predicate]; 


    // Edit the sort key as appropriate. 
    NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES]; 
    NSArray *sortDescriptors = @[sortDescriptor]; 

    [fetchRequest setSortDescriptors:sortDescriptors]; 

    // Edit the section name key path and cache name if appropriate. 
    // nil for section name key path means "no sections". 
    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"SubjectMaster"]; 
    aFetchedResultsController.delegate = self; 
    self.fetchedResultsController = aFetchedResultsController; 

    NSError *error = nil; 
    if (![self.fetchedResultsController performFetch:&error]) { 
     // Replace this implementation with code to handle the error appropriately. 
     // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. 
     NSLog(@"Unresolved error %@, %@", error, [error userInfo]); 
     abort(); 
    } 

    return _fetchedResultsController; 
} 

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

} 

- (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:@[newIndexPath] withRowAnimation:UITableViewRowAnimationTop]; 
      break; 

     case NSFetchedResultsChangeDelete: 
      [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight]; 
      break; 

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

     case NSFetchedResultsChangeMove: 
      [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      [tableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
    } 
} 

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

不想把这个放在答案中 - 你确定核心数据的提取是导致问题的原因吗?如果您没有实例化Fetched Results Controller,问题是否会消失? – ChrisH 2013-02-11 15:06:14

回答

2

与核心数据的一般规则是每一个线程管理对象上下文,每MOC一个线程。考虑到这一点,您需要在主线程上执行Fetched Results Controller的提取操作,因为这是将与FRC的Managed Objects交互的线程。 (请参阅Core Data Programming Guide - Concurrency with Core Data

如果您遇到动画性能问题,您应该考虑如何确保在推送视图之前或之后执行提取操作。通常情况下,您将在视图控制器的viewDidLoad:中执行提取操作,导航控制器在提取完成之前不会推送视图。

+0

问题是 - 我如何使用NSFetchedResultsController在后台获取数据?有两种MOC(背景和主队列)合并或任何其他技巧的现代模式? – adnako 2013-11-06 13:39:29

+0

@adnako试试这个http://www.duckrowing.com/2010/03/11/using-core-data-on-multiple-threads/ – Suny 2013-11-15 14:36:21

+0

你的答案中的链接已损坏。 – eLillie 2017-03-10 19:05:14

0

你可以通过核心数据多线程行为这个非常nice帖子。

希望它有助于..!

16

TL; DR;在主队列上使用上下文没有充分的理由。

可以使用NSFetchedResultsController在背景数据取

绝对。 NSFetchedResultsController可以与私人队列上下文一起使用。事实上,这样做的时候非常高兴和高效。 There is a bug,它可以防止NSFetchedResultsController在使用专用队列时使用它的缓存,但缓存不会像在iOS 3.0中那样赢得比赛。设置零的cacheName,你会没事的。

1.使用NSPrivateQueueConcurrencyType创建上下文。最好不是你用于IO的那个。

2.使用该上下文创建抓取的结果控制器,并且缓存名称为nil。

3.执行您最初从performBlock:块中获取:

[[[self fetchedResultsController] managedObjectContext] performBlock:^{ 
    NSError *fetchError = nil; 
    if (![self fetchedResultsController] performFetch:&error]){ 
     /// handle the error. Don't just log it. 
    } else { 
     // Update the view from the main queue. 
     [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
      [tableView reloadData]; 
     }]; 
    } 
}]; 

4.您的委托回调所有现在从上下文的队列发生。如果您正在使用它们来更新视图,则可以通过像上面所看到的那样分派到主队列来执行此操作。

5. ...

6.利润!

您可以阅读更多关于此here

+0

您的意思是: [[fetchedResultsController managedObjectContext] performBlock^{...}];? – 2014-09-27 13:36:14

+0

是的。更新了答案。 – quellish 2014-09-27 17:21:48

+0

应该执行BlockAndWait:OR performBlock:尽可能使用,假设MOC是在“NSPrivateQueueConcurrencyType”或“NSMainQueueConcurrencyType”下创建的? – 2014-09-27 17:45:35