iOS5系でNSURLConnectionを使った場合のタイムアウト設定方法
ネットワーク処理でNSURLConnectionを使っているとiOS5系でのみ設定したタイムアウト時間よりも遥かに長く待たされた。
どうやらアップルの仕様でNSURLRequestでPOSTかつbodyに値を設定するとそうなるみたい。
問題のコードは下記
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] initWithURL:url] autorelease]; [request setTimeoutInterval:10]; //タイムアウトを10秒に設定 [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"]; [request setHTTPMethod:@"POST"]; //POSTメソッド [request setHTTPBody:postData]; //bodyに値をセット NSURLResponse* response; NSError* error = nil; //通信 NSData* result = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:&response error:&error];
ネットワーク状態の悪いところでやると240秒待たされた。
クラスメソッドを使っていたのでキャンセルもできない。
(sendSynchronousRequestなので同期処理だが、呼び出し元でバックグラウンドスレッドを生成している。)
iOS5系のタイムアウト処理を10秒にしたかったのでタイマーで下記のように設定しました。
NSURLConnection *connection = [[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO] autorelease];
self.connection = connection; //NSURLConnectionインスタンス
if (self.timer) {
[self.timer invalidate];
}
//iOS5系はタイムアウト時間を設定できないので非同期メソッドとタイマーを使用して対応
self.timer = [NSTimer scheduledTimerWithTimeInterval:10 target:self selector:@selector(connectionTimeout:) userInfo:nil repeats:NO];
self.receiveData = [NSMutableData data]; //デリゲートでレスポンスをappendするインスタンス
self.error = nil;
[self.connection start];
self.isConnection = YES; //ネットワーク処理が終わったかどうかの判定
//終わるまで待つ
while (self.isConnection) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];
}
-------------------------------------------------------------------------------------------------------------
//タイマーから呼ばれるconnectionTimeoutメソッド
- (void)connectionTimeout:(NSTimer *)timer
{
[self.connection cancel];
[timer invalidate];
timer = nil;
self.isConnection = NO;
}
#pragma mark - NSURLConnection Delegate
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//receiveData初期化
[self.receiveData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.receiveData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[self.timer invalidate];
self.timer = nil;
self.isConnection = NO;
}
ようはNSURLConnectionのインスタンス生成して非同期で通信させてタイマーで設定した時間までに何も反応無ければキャンセルするってだけです。
本来バックグラウンドスレッドで同期ネットワーク処理をしたかったのでNSRunLoopを使って終わるまで待ったりしています。