NSFetchedResultsController:在后台线程中获取 [英] NSFetchedResultsController: Fetch in a background thread

查看:21
本文介绍了NSFetchedResultsController:在后台线程中获取的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个或多或少基本的 UITableViewController 和一个 NSFetchedResultsController.UITableViewController 被推送到 navigationController's 堆栈上.但是推送动画并不流畅,因为 NSFetchedResultsController 的获取是在主线程上执行的,因此会阻塞 UI.

I have a more or less basic UITableViewController with a NSFetchedResultsController. The UITableViewController is pushed onto the navigationController's stack. But the push animation isn't smooth because the fetch of NSFetchedResultsController is performed on the main thread, and therefore blocks the UI.

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

My question is: How can I perform the fetch of the NSFetchedResultsController in a background thread to keep the animation smooth?

NSFetchedResultsController 和委托方法如下所示:

The NSFetchedResultsController and the delegate methods look like this:

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

推荐答案

Core Data 的一般规则是每个线程一个托管对象上下文,每个 MOC 一个线程.考虑到这一点,您需要在主线程上对 Fetched Results Controller 执行 fetch,因为这是将与 FRC 的托管对象交互的线程.(参见 核心数据编程指南 - 与核心数据的并发)

The general rule with Core Data is one Managed Object Context per thread, and one thread per MOC. With that in mind you need to perform the fetch for the Fetched Results Controller on the main thread, as this is the thread that will be interacting with the FRC's Managed Objects. (See Core Data Programming Guide - Concurrency with Core Data)

如果您在动画方面遇到性能问题,您可能应该考虑确保在推送视图之前或之后执行提取的方法.通常,您会在视图控制器的 viewDidLoad: 中执行获取,并且导航控制器在获取完成之前不会推送视图.

If you are having performance issues with the animation you should probably look at ways to ensure that the fetch is performed before or after the view is pushed. Normally you would perform the fetch in the view controller's viewDidLoad:, and the navigation controller wouldn't push the view until the fetch was complete.

这篇关于NSFetchedResultsController:在后台线程中获取的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆