まず、長すぎる質問で申し訳ありません。
ここには同様の問題について議論する質問がほとんどないことを知っていますが、別のスレッドでのデリゲートと更新を伴う NSFetchedResultsController に関するこれらの話はありません。そして、どの解決策も私を助けてくれませんでした。
既存の質問は次のとおりです。
- NSFetchedResultsController: 更新中に NSManagedObjectContext を使用するとクラッシュする
- NSFetchedResultsController の更新をトリガーしたコア データ属性/プロパティの変更を特定する
- Core Data executeFetchRequest が NSGenericException をスローする (列挙中にコレクションが変更された)
- 列挙中にコレクションが変更されました。どのセットかを判断するには?
- 等
今私の問題について:
- (ソケットを使用して) 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];
}
テーブル ビュー データソース メソッドでは、管理対象オブジェクトを直接操作します。