この種の典型的なタスクを処理する最善の方法が何であるかを理解するのにしばらく時間がかかりました。手がかりは、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 に関する私の最初の回答です。