Webサービスへの非同期呼び出しに AFNetworking を使用しています。これらの呼び出しの一部はチェーン化する必要があり、呼び出しAの結果は呼び出しBによって使用され、呼び出しCによって使用されます。
AFNetworkingは、操作の作成時に成功/失敗ブロックが設定された非同期呼び出しの結果を処理します。
NSURL *url = [NSURL URLWithString:@"http://api.Twitter.com/1/statuses/public_timeline.json"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
NSLog(@"Public Timeline: %@", JSON);
} failure:nil];
[operation start];
これにより、ネストされた非同期呼び出しブロックが発生し、すぐに読み取れなくなります。タスクが相互に依存せず、代わりに並行して実行する必要があり、実行がすべての操作の結果に依存する場合は、さらに複雑になります。
より良いアプローチは、制御フローをクリーンアップするために promises フレームワークを活用することであるように思われます。
MAFuture に出くわしましたが、AFNetworkingと統合する最善の方法がわかりません。非同期呼び出しは複数の結果(成功/失敗)をもたらす可能性があり、戻り値がないため、理想的な適合とは思えません。
任意のポインタやアイデアをいただければ幸いです。
まだ使用していませんが、 Reactive Cocoa はあなたが説明したとおりに動作するように設計されているようです。
このための軽量ソリューションを作成しました。それはシーケンサーと呼ばれ、 github にあります。
これにより、API呼び出し(またはその他の非同期コード)の連鎖が簡単かつ簡単になります。
AFNetworkingを使用する例を次に示します。
Sequencer *sequencer = [[Sequencer alloc] init];
[sequencer enqueueStep:^(id result, SequencerCompletion completion) {
NSURL *url = [NSURL URLWithString:@"https://alpha-api.app.net/stream/0/posts/stream/global"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
completion(JSON);
} failure:nil];
[operation start];
}];
[sequencer enqueueStep:^(NSDictionary *feed, SequencerCompletion completion) {
NSArray *data = [feed objectForKey:@"data"];
NSDictionary *lastFeedItem = [data lastObject];
NSString *cononicalURL = [lastFeedItem objectForKey:@"canonical_url"];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:cononicalURL]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
completion(responseObject);
} failure:nil];
[operation start];
}];
[sequencer enqueueStep:^(NSData *htmlData, SequencerCompletion completion) {
NSString *html = [[NSString alloc] initWithData:htmlData encoding:NSUTF8StringEncoding];
NSLog(@"HTML Page: %@", html);
completion(nil);
}];
[sequencer run];
GowallaでAFNetworkingを使用して、成功ブロックで呼び出しを連鎖させることは珍しくありませんでした。
私のアドバイスは、ネットワーク要求とシリアル化をモデルのクラスメソッドにできる限り考慮に入れることです。次に、サブレケットを作成する必要があるリクエストの場合、成功ブロックでそれらのメソッドを呼び出すことができます。
また、まだ使用していない場合は、AFHTTPClient
を使用するとこのような複雑なネットワークの相互作用が大幅に簡素化されます。
PromiseKit 便利かもしれません。これは、最も人気のあるPromise実装の1つであるように思われ、AFNetworkingなどのライブラリと統合するためのカテゴリを作成しているものもあります。 PromiseKit-AFNetworking を参照してください。
ここGithubには、CommonJSスタイルのpromiseのObjective-C実装があります。
https://github.com/mproberts/objc-promise
例(Readme.mdから取得)
Deferred *russell = [Deferred deferred];
Promise *promise = [russell promise];
[promise then:^(NSString *hairType){
NSLog(@"The present King of France is %@!", hairType);
}];
[russell resolve:@"bald"];
// The present King of France is bald!
私はまだこのライブラリを試していませんが、この少し圧倒的な例にもかかわらず、「有望」に見えます。 (申し訳ありませんが、私は抵抗できませんでした)。
NSBlockOperation
とsemaphore
を組み合わせて、次のことができます。
- (void)loadDataByOrderSuccess:(void (^)(void))success failure:(void (^)(void))failure {
// first,load data1
NSBlockOperation * operation1 = [NSBlockOperation blockOperationWithBlock:^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[self loadData1Success:^{
dispatch_semaphore_signal(sema);
} failure:^{
!failure ?: failure();
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}];
// then,load data2
NSBlockOperation * operation2 = [NSBlockOperation blockOperationWithBlock:^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[self loadData2Success:^{
dispatch_semaphore_signal(sema);
} failure:^{
!failure ?: failure();
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}];
// finally,load data3
NSBlockOperation * operation3 = [NSBlockOperation blockOperationWithBlock:^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[self loadData3Success:^{
dispatch_semaphore_signal(sema);
} failure:^{
!failure ?: failure();
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
!success ?: success();
}];
[operation2 addDependency:operation1];
[operation3 addDependency:operation2];
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation1, operation2, operation3] waitUntilFinished:NO];
}