6

だから私が達成しようとしている目的

は、AFNetworkingとMagical Recordを使用してバックグラウンドで完了することになっている同期プロセスですが、NSFetchedResultsControllerに接続されているView Controllerが現在開いているか、開いている(ただしポップされている)場合、永続的なハングが発生します。

アプリは、ユーザーが最初に電話を開いたときに同期し、その後、MagicalRecordフレームワークを介してCoreData永続性ストアに常にあるデータを使用します。次に、ユーザーがデータが最新バージョンであることを確認したい場合は、設定に移動して[再同期]をクリックします。これにより、次のコードが実行されます。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
    [[CoreDataOperator sharedOperator] commenceSync:self];
});

これにより、シングルトンCoreDataOperator(NSObjectのサブクラス-おそらくNSOperationである必要がありますか?)を使用して同期プロセスが開始され、次のコードが起動されます。

[[ApiClient sharedClient] getDataRequest];

次に、シングルトンAFHTTPClientサブクラスでこの不良少年を解雇します。

[[ApiClient sharedClient] postPath:url parameters:dict
 success:^(AFHTTPRequestOperation *operation, id responseObject) {
 [request.sender performSelector:request.succeeded withObject:response];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
 [request.sender performSelector:request.failed withObject:response];
}
 ];

これは事実上次のように述べています。AFHTTPClientはこの情報を投稿し、成功したら、提供されたセレクターに情報を返します(これは一般的であることを理解していますが、要求は問題ではありません)

現在、AFNetworkingは、すべての完了セレクター(この場合は特に成功と失敗)がメインスレッドで呼び出されるようにコーディングされています。したがって、メインスレッドのブロックを防ぐために、応答を処理して保存の準備をするコードがバックグラウンドスレッドに返送されます。

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    id responseData;
    [self clearAndSaveGetData:responseData];
});

次に、Magical Recordフレームワークを使用して保存を行う関数を呼び出します(まだバックグラウンドスレッドにあります)。

NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread];

[DATAENTITY truncateAllInContext:localContext];
<!--- PROCESS DATA INTO DATAENTITY -->
[localContext saveNestedContexts];

私が選んだsaveNestedContextsのは、バックグラウンドで作業しているので、デフォルトのコンテキストまでプッシュしたかったからです。これは、親コンテキストであると想定していますか?(しかし、これは今のところ問題ではありません)。

現在、このデータは数千行になる可能性があるため、NSFetchedResultsControllerを使用してこのデータに安全かつ効率的にアクセスし、設定やメインページとは異なるビューコントローラーで使用されます。

これが3つのケースです:

  1. FRCを含むViewControllerにアクセスしていません(表示されておらず、以前は表示されていません) -バックグラウンド同期は問題なく機能しますが、スタックの節約によるわずかな遅延はありません。
  2. FRCを含むViewControllerがアクセスされ、現在表示されています-コンテキストの更新を受信して​​いるFRCのように見えるため、バックグラウンド同期プロセスがハングします。
  3. FRCを含むViewControllerは以前にアクセスされていましたが、現在は表示されていません(VCは次のコードを使用してポップ[self.navigationController popViewControllerAnimated:YES];されました:)そしてFRCはViewDidUnloadでnilに設定されています-FRCのように見えるため、バックグラウンド同期プロセスがハングしますコンテキストの更新を受信します(ケース2の場合と同様)。

[追記:数分間ハングした後、デバッガーを強制終了すると、次のコードでSIGKILLが生成されます。そのため、FRCがコンテキストの更新を受信して​​ハングしていると確信しています。

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller 
{
    UITableView *tableView = controller == self.fetchedResultsController ? self.tableView : self.searchDisplayController.searchResultsTableView;
    [tableView endUpdates]; <---- SIGKILL
}

注意すべきその他の重要な情報:

  • 私は2つの別々のFRCを使用しています。1つは通常のALLデータ用で、もう1つは検索用です。
  • FRCと個別の検索FRCの両方にキャッシュを使用しています。これは、適切なタイミングで(このコンテキストが更新される前に)パージされます。
  • FRCはメインスレッドでフェッチしており(数千行のデータがある場合はわずかにハングします)、バックグラウンドでフェッチすることを検討していますが、現在は実装されていません。

質問):

  1. VCが表示されている、またはポップされている場合に、このハングが発生するのはなぜですか?
  2. FRCが保存をリッスンせず、保存が完了するまで保持しているものを使用してからデータを更新するようにするにはどうすればよいですか(これがすでに発生してハングを引き起こしている場合を除く)。
  3. バックグラウンドフェッチを実装する(FRCでVCを開いて数千行のデータにアクセスするときのラグは、キャッシュと述語/セクションヘッダーの削減(1〜4秒)でも、顕著なラグを作成するため)実行可能でしょうか?複雑すぎますか?どのようにそれを行うことができますか?

ありがとう、私が私の質問で十分に詳細にされたことを望みます。

4

1 に答える 1

1

私はこの質問が古いことを知っていますが、これはMagicalRecordの一般的な落とし穴なので、おそらく次のことが誰かを助けるでしょう。

この問題はfetchRequest、FRCと保存操作が互いにデッドロックしていることが原因で発生します。以下は私のためにそれを解決しました:

最新のMagicalRecordリリース(執筆時点では2.1)に更新します。

次に、以下を使用してすべてのバックグラウンド保存を実行します。

MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
  // create new objects, update existing objects. make sure you're only 
  // accessing objects inside localContext 
  [myObjectFromOutsideTheBlock MR_inContext:localContext]; //is your friend
}
completion:^(BOOL success, NSError *error) {
  // this gets called in the main queue. safe to update UI.
}];
于 2013-05-22T18:20:42.073 に答える