5

まず、長すぎる質問で申し訳ありません。

ここには同様の問題について議論する質問がほとんどないことを知っていますが、別のスレッドでのデリゲートと更新を伴う NSFetchedResultsController に関するこれらの話はありません。そして、どの解決策も私を助けてくれませんでした。
既存の質問は次のとおりです。

今私の問題について:

  • (ソケットを使用して) Web からコア データ オブジェクトを更新する別のスレッドがあります。
  • 同じコア データ オブジェクトからのデータを表示するビュー コントローラーはほとんどありません (各タブには、フィルター処理されたデータを表示するビュー コントローラーが含まれています)。
  • 各View Controllerには独自のインスタンスがNSFetchedResultsControllerあり、デリゲートはselfに設定されています。

別のスレッドでデータを更新すると例外が発生することwas mutated while being enumeratedがあり、アプリがクラッシュすることがあります。

私はそれを修正しようとするために多くのコード操作を行いましたが、何も役に立たないようです.
テーブル ビュー データソース メソッドから管理対象オブジェクトを直接使用しないようにしました。その代わりに、辞書のリストを保持する配列を作成しました。didChangeObject上記のメソッドでこれらの辞書を埋めます。このようにして、View Controller で管理対象オブジェクトにまったく触れません。

そして、おそらくデータを常に反復する NSFetchedResultsController に問題があることを理解しました。そして、これは別のスレッドで私のデータ更新と競合するオブジェクトです。

問題は、デリゲートを持つ NSFetchedResultsController を取得したら、別のスレッドでコア データ オブジェクトを更新するにはどうすればよいかということです (つまり、データを「監視」し、デリゲートを常に更新することを意味します)。

NSFetchedResultsControllerDelegate の実装:

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
    [self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
    if ( self.tabBarController.selectedIndex == 0 ) {
        UITableView *tableView = self.tableView;
        @try {
            switch(type) 
            {
                case NSFetchedResultsChangeInsert:
                    [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
                    break;
                case NSFetchedResultsChangeDelete:
                    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                    break;
                case NSFetchedResultsChangeUpdate:
                    [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
                    break;
                case NSFetchedResultsChangeMove:
                    [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
                    [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade];
                    break;
            }
        }
        @catch (NSException * e) {
            NSLog(@"Exception in didChangeObject: %@", e);
        }
    }
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
    if ( self.tabBarController.selectedIndex == 0 ) {
        @try {
            switch(type) {
                case NSFetchedResultsChangeInsert:
                    [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
                    break;
                case NSFetchedResultsChangeDelete:
                    [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
                    break;
            }           
        }
        @catch (NSException * e) {
            NSLog(@"Exception in didChangeSection: %@", e);
        }
    }
}

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

テーブル ビュー データソース メソッドでは、管理対象オブジェクトを直接操作します。

4

1 に答える 1

12

ここに2つの別々の質問があります。まず、変更エラーが発生した場合は、そのセット/配列/関係を反復処理しているときに、セットまたは配列(または関係)を変更していることを意味します。あなたがそれをしている場所を見つけて、それをやめなさい。それが唯一の解決策です。

あなたのアップデートについて。あなたのバックグラウンドNSManagedObjectContextは定期的に保存する必要があります。メインスレッドはリッスンしている必要があり、メインスレッドを受信すると、メインスレッドNSManagedObjectContextDidSaveNotificationのメインを呼び出します(通知はバックグラウンドスレッドで受信される可能性が高いため)。これを介して、をパラメーターとして受け取ります。これにより、すべてのインスタンスがデリゲートメソッドを起動します。 NSManagedObjectContext -mergeChangesFromContextDidSaveNotification:NSNotificationNSFetchedResultController

そのような単純な。

アップデート

お返事ありがとうございます。バックグラウンドスレッドでNSManagedObjectContextを更新すると、例外がスローされます。両方のスレッドで同じNSManagedObjectContextを使用しています。アプリはリアルタイムアプリに可能な限り近づける必要があります。常に更新され、テーブルはすぐに更新される必要があります。まったく保存しません-NSManagedObjectContextのみを更新します。誰かがNSManagedObjectContextのインスタンスを分離するために使用したが、変更をマージした後も同じ例外を受け取るという質問の1つを見ました。したがって、2つの別々のNSManagedObjectContextを使用することをお勧めしますか?

まず、Appleのドキュメント(または私の本:)からCoreDataのマルチスレッドについて読んでください。

NSManagedObject次に、はい、スレッドごとに1つのコンテキストが必要です。これは、Core Dataとマルチスレッドのゴールデンルールの1つです(もう1つは、スレッド間でインスタンスを渡さないことです)。それがおそらくクラッシュの原因であり、そうでない場合は、将来的にクラッシュの原因になるでしょう。

アップデート

大量のデータがあり、テーブル内の変更/新規/削除されたアイテムのみを更新します。保存を開始すると、パフォーマンスが低下しますか?

いいえ、更新のみがスレッド全体に伝播されます。データストア全体が再読み取りされないため、メインスレッドでUIを更新し、UIのパフォーマンスが向上するように見えるため、保存を小さなチャンクに分割すると、実際にパフォーマンスが向上します。

ただし、アプリが完成する前にパフォーマンスを心配することは、避けるべき事前最適化です。何がうまくいくのか、何がうまくいかないのかを推測することは、一般的に悪い考えです。

于 2010-07-29T16:47:01.900 に答える