ねえ、私はここで私たちのアプリのモデル層に取り組んでいます。
要件の一部は次のとおりです。
サーバー駆動型のユーザーエクスペリエンスの構築に関するWWDC10セッション117をチェックアウトし、 Objective Resource =、 コアリソース 、および RestfulCoreData フレームワーク。
Objective Resourceフレームワークはそれ自体ではCore Dataと通信せず、単にREST=クライアント実装です。CoreResourceとRestfulCoreDataはすべて、コード内でCore Dataと通信することを前提としており、すべて解決しますモデルレイヤーの背景にあるナットとボルト。
これまでのところすべてが大丈夫に見え、最初はCore ResourceまたはRestfulCoreDataのいずれかが上記の要件のすべてをカバーしますが、...それらがどれも正しく解決されないように思われるいくつかのことがあります:
コアリソースは、マネージオブジェクトコンテキストで- (BOOL)save:(NSError **)error
を呼び出すと、すべてのリクエストをサーバーに発行するため、サーバーへのリクエストの正しいNSErrorインスタンスを何らかの方法で失敗させることができます。ただし、保存操作が完了するまで呼び出しスレッドをブロックします。不合格。
RestfulCoreDataは-save:
呼び出しをそのまま保持し、クライアントスレッドに追加の待機時間を導入しません。単にNSManagedObjectContextDidSaveNotification
を監視し、対応するリクエストを通知ハンドラーでサーバーに発行します。しかし、このように-save:
呼び出しは常に正常に完了します(保存された変更でCore Dataに問題がない場合)。いくつかの404
または421
またはその他のサーバー側エラーが発生しました。さらに、ローカルストレージではデータが更新されますが、サーバーは変更を認識しません。不合格。
したがって、私はこれらすべての問題に対処するための可能な解決策/一般的な実践を探しています:
-save:
呼び出しごとにブロックしないようにします。何か案は?
このユースケースについては、RestKit( http://restkit.org )を実際に見てください。これは、リモートJSONリソースのモデリングとローカルCore Dataバックアップキャッシュへの同期の問題を解決するように設計されています。使用可能なネットワークがないときにキャッシュから完全に動作するオフラインモードをサポートします。すべての同期はバックグラウンドスレッド(ネットワークアクセス、ペイロード解析、管理対象オブジェクトコンテキストのマージ)で行われ、デリゲートメソッドの豊富なセットがあるため、何が起こっているのかを知ることができます。
3つの基本的なコンポーネントがあります。
NSOperation + NSOperationQueueは、ネットワーク要求を整然と保つのに役立ちます。デリゲートプロトコルは、UIクラスがネットワーク要求の状態を理解するのに役立ちます。次のようなものです。
@protocol NetworkOperationDelegate
- (void)operation:(NSOperation *)op willSendRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
- (void)operation:(NSOperation *)op didSuccessfullySendRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
- (void)operation:(NSOperation *)op encounteredAnError:(NSError *)error afterSendingRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
@end
もちろん、プロトコル形式は特定のユースケースに依存しますが、基本的に作成するのは、変更をサーバーに「プッシュ」できるメカニズムです。
次に、考慮するUIループがあります。コードをきれいに保つには、save:を呼び出して、変更を自動的にサーバーにプッシュするのが良いでしょう。このためにNSManagedObjectContextDidSave通知を使用できます。
- (void)managedObjectContextDidSave:(NSNotification *)saveNotification {
NSArray *inserted = [[saveNotification userInfo] valueForKey:NSInsertedObjects];
for (NSManagedObject *obj in inserted) {
//create a new NSOperation for this entity which will invoke the appropraite rest api
//add to operation queue
}
//do the same thing for deleted and updated objects
}
ネットワーク操作を挿入するための計算オーバーヘッドはかなり低くなりますが、UIに顕著な遅延が生じる場合は、保存通知からエンティティIDを取得して、バックグラウンドスレッドで操作を作成するだけです。
REST APIがバッチ処理をサポートしている場合、配列全体を一度に送信し、複数のエンティティが同期されたことをUIに通知することもできます。
私が予見し、「実際の」解決策がない唯一の問題は、ユーザーが変更がサーバーにプッシュされて変更がさらに許可されるのを待ちたくないということです。私が遭遇した唯一の良いパラダイムは、ユーザーがオブジェクトを編集し続けることを許可し、必要に応じて編集をまとめてバッチ処理できることです。つまり、すべての保存通知をプッシュしません。
これは同期の問題になり、簡単に解決できるものではありません。 iPhone UIで1つのコンテキストを使用してから、別のコンテキスト(および別のスレッド)を使用して、Webサービスからデータをダウンロードします。すべてが完了したら、以下で推奨される同期/インポートプロセスを実行し、すべてが適切にインポートされたらUIを更新します。ネットワークへのアクセス中に問題が発生した場合は、非UIコンテキストで変更をロールバックしてください。それはたくさんの仕事ですが、私はそれがそれに近づくための最良の方法だと思います。
他のスレッド(実際のサーバーインタラクションが発生するスレッド)で実行するコールバック関数が必要であり、UIスレッドによって定期的にチェックされる結果コード/エラー情報をセミグローバルデータに配置します。フラグとして機能する数値のワーティングがアトミックであること、または競合状態になることを確認してください-エラー応答が32バイトの場合、intが必要である(whihcにはアトミックアクセスが必要です)とし、そのintを保持する大きなデータブロックが書き込まれるまでoff/false/not-ready状態になってから、「true」と書き込んでスイッチを切り替えます。
クライアント側の相関保存については、そのデータを保持するだけで、サーバーからOKになるまで保存せずに、ロールバックオプションの種類を確認する必要があります-削除方法はサーバー障害です。
完全な2フェーズコミット手順(サーバーサーバーからの信号の後にクライアントの保存または削除が失敗する可能性があります)を行わない限り、100%安全になることはありませんが、少なくとも2回サーバーにアクセスする必要があることに注意してください(唯一のロールバックオプションが削除の場合、4の費用がかかる場合があります)。
理想的には、別のスレッドで操作のブロッキングバージョン全体を実行しますが、そのためには4.0が必要です。