ブロックについて読んだところですが、ブロックは通常の方法で情報をカプセル化しているだけで、独自の強力な参照データを使用していることを理解しました。ブロックの良い使い方は何だろう?
3 に答える
これは、私のプロジェクトに適用されたブロックの使用法です。デリゲートとプロトコルを置き換える (特定の状況で)。
問題
サーバーからデータを非同期にロードする必要があるとします。パス (データを含む) に PUT する必要があるメソッドがあり、最終的にタスクが完了すると、メソッドの呼び出し元に結果が送信されます。
デリゲートとプロトコル ソリューション
クライアントからのメソッド シグネチャは次のAppClient
とおりです。
- (void)putToPath:(NSString *)path withData:(id)data;
このメソッドは非同期であるため、戻り値にデータを含めることはできません (つまり、次のコード行を実行するなど、他のことを行うためにタスクが完了するのを待ちません)。代わりに、プロトコルを構築します。
@protocol AppClientRequestDelegate
- (void)appClient:(AppClient *)appClient didPutToPath:(NSString *)path withData:(id)sentData andReturnedData:(id)recievedData;
@end
次に、AppClient
クラスは次のようなプロパティを作成します。
@property (weak, nonatomic)id<AppClientRequestDelegate> requestDelegate;
メソッドの呼び出し元は、putToPath...
AppClient のrequestDelegate
プロパティのインスタンスを に設定し、メソッドを実装してから、およびパラメータself
を使用して正しい要求を検証し、パラメータを使用して何らかの処理を行います。path
sentData
receivedData
呼び出し元のコードは次のようになります。
- (void)syncData:(id)data {
[self.appClient putPath:@"/posts/9" withData:data];
}
- (void)appClient:(AppClient *)appClient didPutToPath:(NSString *)path withData:(id)sentData andReturnedData:(id)recievedData {
if (/*path and sentData are right*/) {
// Do something with recievedData
}
}
これはすべて素晴らしいことですが、同じパスに多数の PUT リクエストがあり、プロトコルの実装内でリクエストを区別しようとする場合は最悪です。デリゲート メソッドと各リクエストの ID を指定するメソッドの両方に別のパラメーターを追加できると思いますがputToPath...
、それは面倒で混乱を招きます。
もう 1 つの潜在的な懸念は、このアプリ全体で非同期読み込みを広く使用している場合です。その結果、多数のデリゲートとプロトコルが発生する可能性があります。
ブロック ソリューション
メソッド シグネチャを拡張して、ブロックを含めます。
- (void)putToPath:(NSString *)path withData:(id)data completion:(void (^)(id returnedData))completion;
確かに、この構文は非常に困難ですが、プロトコルからのすべての情報が含まれているだけでなく、メソッドの呼び出し元がすべてのロジックを 1 つのメソッドに凝縮できるため、そのメソッド内で呼び出されたローカル変数がブロックのスコープに含まれます。実装。
呼び出し元のコードは次のようになります。
- (void)syncData:(id)data {
[self.appClient putToPath:@"/posts/9" withData:data completion:^(id returnedData) {
// Do something with returnedData
}];
}
結論
あなたはブロックの上手な使い方を求めましたが、これはとてもいい使い方だと思います。あなたには当てはまらないかもしれませんが、コードの量を削減するだけでなく、コードをより読みやすく堅牢にする方法を見ることができます。