複数のHTTPリクエストを投げるときはNSOperationを使おう
現在、iPhone SDKで簡単なマッシュアップアプリを作成中です。マッシュアップアプリなので一度の操作でいろんなところにHTTPリクエストを投げる必要があるのですが、そんな時にNSOperationを使うとものすごく便利です。NSOperationを使うことで複数のHTTPリクエストを同時並行で処理できるようになります。
使い方はいたって簡単でNSOperationクラスのサブクラスを用意して、そこでHTTPリクエストの処理をしてあげるだけです。
@interface RequestOperation : NSOperation { NSURL *url; NSMutableData *responseData; BOOL isExecuting, isFinished; } - (id)initWithURL:(NSURL *)targetUrl; @end @implementation RequestOperation : : KVO 系のオーバラードメソッドの実装は省略します。 : - (id)initWithURL:(NSURL *)targetUrl { self = [super init]; if (self) { url = [targetUrl retain]; } isExecuting = NO; isFinished = NO; return self; } - (void)dealloc { [url release], url = nil; [super dealloc]; } - (void)start { [self setValue:[NSNumber numberWithBool:YES] forKey:@"isExecuting"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self]; if (conn != nil) { // iOS4 以降注意! // NSURLConnection は RunLoop をまわさないと並列実行モードで動かない do { [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; } while (isExecuting); } } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { NSLog(@"%@", @"レスポンス"); responseData = [[NSMutableData alloc] init]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [responseData appendData:data]; } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]; NSLog(@"%@", responseString); [responseData release]; [responseString release]; [self setValue:[NSNumber numberWithBool:NO] forKey:@"isExecuting"]; [self setValue:[NSNumber numberWithBool:YES] forKey:@"isFinished"]; } @end
isConcurrentメソッドをオーバライドしてYESを返すようにしないと以下のようなエラーが出るので注意してください(デフォルトではisConcurrentはNOになっています)。
_NSAutoreleaseNoPool(): Object 0x18a140 of class NSURLConnection autoreleased with no pool in place - just leaking
isConcurrentがNOの場合同期処理しかできなくなります。NSURLConnectionクラスのconnectionWithRequest:delegate:メソッドを呼ぶと非同期のイベントが発生するのでここは必ずYESにしてください(私はこれで2時間ハマりました)。
あとはNSOperationQueueのインスタンスを生成し処理を依頼すればOKです。
@interface HogeViewController : UIViewController { NSOperationQueue* _queue; } // ボタンクリック処理 - (IBAction)buttonClicked:(id) sender; @end @implementation HogeViewController - (IBAction)buttonClicked:(id) sender { _queue = [[NSOperationQueue alloc] init]; // 1つ目のリクエスト [RequestOperation *ope1 = [[RequestOperation alloc] initWithURL:[NSURL URLWithString:@"http://www.flickr.com"]]; [ope1 autorelease]; // 2つ目のリクエスト [RequestOperation *ope2 = [[RequestOperation alloc] initWithURL:[NSURL URLWithString:@"http://www.yahoo.com"]]; [ope2 autorelease]; // 処理が開始される [_queue addOperation:ope1]; [_queue addOperation:ope2]; } @end
NSOperationQueueインスタンスのaddOperation:メソッドを呼び出すと処理が開始されます。1つ1つの処理は別スレッドで処理されます。