3

非同期リクエストが関係する割り当てを処理するための信頼できる設計を探しています。さらに明確にするために、データ管理を処理するクラスがあります。これはシングルトンであり、iPhone アプリケーション全体で使用される多くの最上位データが含まれています。

ビュー コントローラーは、次のようなことを行う場合があります。

users = [MySingleton sharedInstance].users;

MySingleton は、合成されたユーザーの getter をオーバーライドし、設定されているかどうかを確認します。設定されていない場合は、非同期要求を開始する接続マネージャー (NSURLConnection とそのデリゲート メソッドのラッパー) と通信し、ここから問題が発生します。「ユーザー」がいつ利用可能になるかは保証できません。リクエストを同期に変更することもできますが、特に帯域幅がすでに制限されているモバイル環境では、ユーザー エクスペリエンスに直接影響します。

ある時点で、利用可能になるか nil になるまでユーザーを返さない、ある種のロック/同期コードをゲッターで実行できるようにする必要があります。

NSURLConnection がデータを利用できるようになったら、応答オブジェクトを使用して何か/どこかをコールバックし、ゲッターにデータが利用可能であることを知らせる必要があります.失敗したか成功したか.

これを処理するための提案はありますか?

4

3 に答える 3

3

この問題は、さまざまなアプリでいくつかの方法で解決しました。

1 つの解決策は、次のようにオブジェクトとセレクターを渡して通知することです。

- (id)getUsersAndNotifyObject:(id)object selector:(SEL)selector

ただし、これにより、nice プロパティの動作が損なわれます。メソッドをプロパティとして保持したい場合は、キャッシュされたデータまたは nil のいずれかで、メソッドがすぐに返されるようにします。ネットワークに接続する必要がある場合は、非同期で行い、KVO または NSNotificationCenter を介して変更されたデータをアプリの残りの部分に知らせます。(Cocoa Bindings は Mac ではオプションですが、iPhone にはありません)。

2 つの方法はかなり似ています。共有インスタンスで更新を登録してから、データを要求します。生の観測可能なプロパティを扱うだけなら、KVO は少し軽量ですが、いくつかの異なるデータに関心がある場合は、NSNotification の方が便利かもしれません。

NSNotification を使用すると、関心のあるキー パスごとにオブザーバーを登録する代わりに、userInfo ディクショナリに変更されたデータを含む 1 つのタイプの通知にクライアント オブジェクトを登録できます。

また、NSNotification を使用すると、障害やその他のステータス情報を単純な KVO よりもはるかに簡単に返すことができます。

KVO法:

// register observer first so you don't miss an update
[[MySingleton sharedInstance] addObserver:self
                               forKeyPath:@"users"
                                  options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
                                  context:&kvo_users_context];
 users = [MySingleton sharedInstance].users;

 // implement appropriate observeValueForKeyPath:ofObject:change:context: method

NSNotification メソッド:

 [[NSNotificationCenter defaultCenter] addObserver:self
                                          selector:@selector(sharedDataChanged:)
                                              name:MySingletonDataUpdatedNotification
                                            object:[MySingletonDataUpdatedNotification sharedInstance]];
 users = [MySingleton sharedInstance].users;

 // implement appropriate sharedDataChanged: method
于 2008-12-16T03:15:22.647 に答える
1

ここでは、デリゲート パターンまたは通知パターンのいずれかを使用できます。

デリゲートは、特定のオブジェクトにユーザーが完了したことを知らせ、通知パターンは知りたいオブジェクトに通知します。状況に応じて、どちらも有効です。

覚えておいてください: アプリに競合の問題がある場合は、アーキテクチャがすべて間違っている可能性があります。

于 2008-12-15T21:51:13.833 に答える
0

この種の典型的なタスクを処理する最善の方法が何であるかを理解するのにしばらく時間がかかりました。手がかりは、Cocoa と CocoaTouch 独自の API の多くの設計にあることがわかりました: delegationです。

Cocoa の API の多くが委譲を使用する理由は、委譲が多くの GUI アプリの非同期性に非常によく適合するためです。

次のようなことをしたいのは、まったく普通のことのように思えます。

users = [MyDataFactory getUsers];

ただし、ご指摘のとおり、getUsersメソッドがいつ終了するかはわかりません。現在、これに対する軽量のソリューションがいくつかあります。amroxは上記の投稿でいくつか言及しました (個人的には、通知はあまり適していないと思いますが、object:selector: パターンは合理的だと思います)。よりエレガントなソリューション。

アプリケーションでどのように処理するかを例として説明しようと思います。

ドメインクラスがあるとしましょうRecipe。レシピは Web サービスから取得されます。通常、モデル内のエンティティごとに 1 つずつ、一連​​のリポジトリクラスがあります。リポジトリ クラスの責任は、エンティティ (またはそれらのコレクション) に必要なデータを取得し、そのデータを使用してオブジェクトを構築し、それらのオブジェクトを別のもの (通常はコントローラーまたはデータ ソース) に渡して使用することです。 )。

私のRecipeRepositoryインターフェースは次のようになります。

@interface RecipeRepository {}
- (void)initWithDelegate:(id)aDelegate;
- (void)findAllRecipes;
- (void)findRecipeById:(NSUInteger)anId;
@end

次に、デリゲートのプロトコルを定義します。現在、これは非公式または正式なプロトコルとして実行できます。この回答には関係のない各アプローチの長所と短所があります。正式なアプローチを使用します。

@protocol RepositoryDelegateProtocol
- (void)repository:(id)repository didRetrieveEntityCollection:(NSArray *)collection;
- (void)repository:(id)repository didRetrieveEntity:(id)entity;
@end

私が一般的なアプローチを採用していることに気付くでしょう。アプリには複数のクラスがあり、それぞれが同じプロトコルを使用する可能性があります (いくつかの共通ロジックをカプセル化XXXRepositoryする基本クラスを抽出することもできます)。EntityRepository

これをコントローラーで使用するには、たとえば、以前は次のようなことを行っていました。

- (void)viewDidLoad 
{
  self.users = [MySingleton getUsers];
  [self.view setNeedsDisplay];
}

次のようにします。

- (void)viewDidLoad 
{
  if(self.repository == nil) { // just some simple lazy loading, we only need one repository instance
    self.repository = [[[RecipeRepository alloc] initWithDelegate:self] autorelease];
  }
  [self.repository findAllRecipes];
}

- (void)repository:(id)repository didRetrieveEntityCollection:(NSArray *)collection;
{
  self.users = collection;
  [self.view setNeedsDisplay];
}

これをさらに拡張して、追加のデリゲート メソッドを使用してある種の「読み込み中」通知を表示することもできます。

@protocol RepositoryDelegateProtocol
- (void)repositoryWillLoadEntities:(id)repository;
@end

// in your controller

- (void)repositoryWillLoadEntities:(id)repository;
{
  [self showLoadingView]; // etc.
}

この設計のもう 1 つの点は、リポジトリ クラスが実際にシングルトンである必要がないことです。それらは必要な場所でインスタンス化できます。それらはある種のシングルトン接続マネージャーを扱うかもしれませんが、この抽象化層ではシングルトンは不要です (そして、可能な限りシングルトンを避けることは常に良いことです)。

このアプローチには欠点があります。各レベルで委任のレイヤーが必要になる場合があります。たとえば、リポジトリは、実際の非同期データ読み込みを行うある種の接続オブジェクトと対話する場合があります。リポジトリは、独自の委任プロトコルを使用して接続オブジェクトとやり取りする場合があります。

その結果、アプリケーション レベルのコードに近づくにつれてますます粗粒度になるデリゲートを使用して、アプリケーションのさまざまなレイヤー全体でこれらのデリゲート イベントを "バブルアップ" する必要があることに気付くかもしれません。これにより、コードを追跡するのが難しくなる可能性のある間接レイヤーが作成される可能性があります。

とにかく、これは SO に関する私の最初の回答です。

于 2009-07-01T23:52:32.953 に答える