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