1

Core Data で TableView を使用するとクラッシュの問題が発生します。ヘルプをいただければ幸いです。シナリオは次のとおりです。

  • Core Data に格納されたデータを表示する UITableViewController があります。ドキュメントの指示に従って、NSFetchResultsController を使用してフェッチを行います。メインスレッドがデータをフェッチするための専用の NSManagedObjectContext があります。

  • データは実際にはサーバーから来ています。アプリケーションが起動すると、コア データ スタックにデータを更新するためのバックグラウンド スレッドがあります。Apple の推奨に従って、バックグラウンド スレッドで別の NSManagedObjectContext を使用してデータを更新します。更新中、古いデータは削除されます。

  • バックグラウンドが変更を保存した後、NSManagedObjectContextDidSaveNotification を使用して、メイン スレッドのコンテキストで mergeChangesFromContextDidSaveNotification を実行するための呼び出しをトリガーしました。

  • UITableViewController を呼び出してリロードする FRC の controllerDidChangeContent も実装されています。

データの更新中に TableView をスクロールすると、アプリがクラッシュし、「コア データが障害を実行できませんでした...」というエラーが表示されます。コードをトレースした後、データの削除を保存するバックグラウンド スレッドとメイン スレッドのコンテキスト マージ操作の間にわずかなタイム ラグがあることが原因であると考えています。このタイム ラグ中に、メイン スレッドのコンテキスト内の管理対象オブジェクトの一部が削除されるため、テーブルがスクロールされ、データ ソース メソッドが削除されたオブジェクトにアクセスすると、アプリがクラッシュします。

私の信念は正しいですか?その場合、このタイムラグをどのように処理すればよいですか?

どうもありがとう。

4

2 に答える 2

0

これを自分で試したことはありませんが、見てくださいNSManagedObjectContextWillSaveNotification。バックグラウンド コンテキストからの通知に登録してみます。次に、ハンドラーで、メイン スレッドへの同期ディスパッチを実行し、コンテキストの削除済みオブジェクト ID を渡すことができます。

- (void)handleBackgroundSave:(NSNotification *)note {
    NSManagedObjectContext *context = [note object];
    NSSet *deletedObjectIDs = [[context deletedObjects] valueForKey:@"objectID"];
    dispatch_sync(dispatch_get_main_queue(), ^{
        // deletedObjectIDs can be passed across threads
        // if NSFetchedResultsController's fetchedObjects contains deleted objects
        // you have to disable it and refetch after DidSaveNotification
    });
}

ディスパッチは同期的であるため、メインスレッドのコンテキストで処理するまで、実際の削除をブロックする必要があります。これはテストされていないため、厄介なデッドロックが発生する可能性があることに注意してください。

指摘する価値のあるもう 1 つのことは、(NSFetchedResultsControllerDelegate実装のように) インタラクティブな更新は、多数のオブジェクト (数百/数千など) が変更されると UI スレッドを占有することです。そのため、更新中にすべてのコア データ オブジェクトを置き換える場合は、frc を無効にすることもできます。すべての WillSave で、すべての DidSave で再フェッチします。

iOS 5 以降をターゲットにする余裕がある場合は、ネストされたコンテキストを検討することをお勧めします。アプローチの概要は次のとおりです

于 2012-09-06T11:41:26.237 に答える
0

うーん、マージが完了するとラグはありません。データは利用可能になります。あなたの問題は別の性質のもののようです。最初に言えることは、あなたのアプローチはやや間違っているということです。更新するためだけにデータを削除するべきではありません。適切な更新が効率的な方法です。

そうは言っても、考慮すべき点がいくつかあります。

  1. メイン スレッドのマネージド オブジェクト コンテキストを使用したマージ呼び出しがメイン スレッドで実行されていることを確認してください。そうしないと、メイン スレッドのコンテキストが呼び出し元のスレッドで NSFetchedResultsController の通知をトリガーし、そのスレッドで NSFetchedResultsController のデリゲート メソッドを呼び出し、メイン スレッドの外部で UI を更新する可能性があります。 .
  2. NSFetchedResults デリゲートで正しい手順を実行していることを確認してください。

これが私の実装です:

#pragma mark -
#pragma mark  NSFetchedResultsControllerDelegate methods
- (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:[NSArray arrayWithObject:newIndexPath]
                         withRowAnimation:UITableViewRowAnimationFade];
        break;
    case NSFetchedResultsChangeDelete:
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                         withRowAnimation:UITableViewRowAnimationFade];
        break;
    case NSFetchedResultsChangeUpdate:
        [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
        break;

    case NSFetchedResultsChangeMove:
        [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
                         withRowAnimation:UITableViewRowAnimationFade];
        [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath]
                         withRowAnimation:UITableViewRowAnimationFade];
        break;
}
}


- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
[self.tableView endUpdates];
}
于 2012-08-16T21:07:19.827 に答える