11

私は多かれ少なかれ基本的UITableViewControllerNSFetchedResultsController。はスタックUITableViewControllerにプッシュされnavigationController'sます。ただし、のフェッチはNSFetchedResultsControllerメインスレッドで実行されるため、プッシュアニメーションはスムーズではなく、UIがブロックされます。

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

4 に答える 4

19

TL; DR; メインキューでコンテキストを使用する正当な理由はありません。

NSFetchedResultsControllerを使用して、バックグラウンドでデータをフェッチできます

絶対。NSFetchedResultsControllerプライベートキューコンテキストで使用できます。実際、そうすることは非常に幸せでパフォーマンスが良いです。プライベートキューを使用しているときにキャッシュを使用できないバグがありNSFetchedResultsControllerますが、iOS3.0の場合ほどキャッシュを使用できません。nilを設定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. 利益!

これについて詳しくは、こちらをご覧ください。

于 2014-07-29T06:50:07.460 に答える
1

Core Dataの一般的なルールは、スレッドごとに1つの管理対象オブジェクトコンテキスト、およびMOCごとに1つのスレッドです。これを念頭に置いて、メインスレッドでFetched Results Controllerのフェッチを実行する必要があります。これは、FRCの管理対象オブジェクトと対話するスレッドであるためです。(CoreDataプログラミングガイド-CoreDataとの並行性を参照してください)

アニメーションでパフォーマンスの問題が発生している場合は、ビューがプッシュされる前または後にフェッチが実行されるようにする方法を検討する必要があります。通常、ビューコントローラのでフェッチを実行しviewDidLoad:、ナビゲーションコントローラは、フェッチが完了するまでビューをプッシュしません。

于 2013-02-11T15:05:35.400 に答える
0

マルチスレッド動作のコアデータに関するこの非常に優れた投稿を読むことができます。

それが役に立てば幸い..!!

于 2013-11-15T14:35:38.410 に答える
0

今日この問題に取り組みました。うまく機能する解決策は、NSFetchedResultsController(FRC)を操作(FRCOperation)でラップすることです。完了後のこの操作の目標は、FRCを保持し、彼を代理することです。

// After completing the operation, it will proxy calls
// from "NSFetchedResultsControllerDelegate.controller(_:didChangeContentWith:)"
protocol FetchOperationDelegate {
    func didChangeContent(snapshot: NSDiffableDataSourceSnapshotReference)
}

// Exemple Operation
protocol FetchOperation: Operation, NSFetchedResultsControllerDelegate {
    weak var delegate: FetchOperationDelegate { get set }
    
    // To get objects from NSFetchedResultsController. Calls always on the main thread
    func object(at indexPath: IndexPath) -> NSManagedObject?
}

論理:

  1. コントローラを開く
  2. 最初にFetchOperationを作成します。FetchOperation.delegate=controllerを設定することを忘れないでください。実行後、FetchOperationDelegateで最初のデータを取得する必要があるためです。
  3. このFetchOperationをcontroller(viewModel)のプロパティに保存します
  4. FetchOperationを実行します。バックグラウンドでのNSFetchedResultsController.performFetch。バックグラウンドコンテキストを使用することを忘れないでください。
  5. たとえば、5秒後にFetchOperationが完了します
  6. 操作からデータを取得してコントローラーを更新します
  7. この瞬間から。FetchOperationDelegate(NSFetchedResultsControllerDelegate)を介してFRCから更新を受信する必要があります。また、メインスレッドのFRCからオブジェクトにアクセスすることもできます。

述語またはソートを変更する必要がある場合。新しいFetchOperationを作成する必要があります。新しい操作が進行している間も、最後のFetchOperationバージョンで作業を続けます。

于 2022-01-11T19:32:02.797 に答える