私のプロジェクトはAFNetworkingを使用しています。
https://github.com/AFNetworking/AFNetworking
タイムアウトをダイヤルダウンするにはどうすればよいですか?インターネットに接続されていないAtmでは、約2分間のように感じてもフェイルブロックはトリガーされません。長い間...
タイムアウト間隔の変更は、ほぼ間違いなく、説明している問題に対する最善の解決策ではありません。代わりに、実際に必要なのは、HTTPクライアントがネットワークに到達できないように処理することです。
AFHTTPClient
には、インターネット接続が失われたときに通知する組み込みのメカニズムが既にあります。-setReachabilityStatusChangeBlock:
。
低速ネットワークでは、リクエストに時間がかかる場合があります。遅い接続を処理する方法を知っているiOSを信頼し、それとまったく接続がないことの違いを伝える方が良いです。
このスレッドで言及されている他のアプローチを避けるべき理由についての私の推論を拡張するために、いくつかの考えがあります:
performSelector:afterDelay:...
は、マルチスレッドアプリケーションでは危険です。これにより、あいまいでデバッグが困難な競合状態が発生します。上記のmatttの答えを見ることを強くお勧めします-この答えは、彼が一般的に言及している問題に反するものではありませんが、元のポスターの質問では、到達可能性をチェックする方がはるかに適しています。
ただし、タイムアウトを設定したい場合は(performSelector:afterDelay:
など、レゴが言及しているプルリクエストでは、これを行う方法をコメントの1つとして説明しています。
NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil];
[request setTimeoutInterval:120];
AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}];
[client enqueueHTTPRequestOperation:operation];
しかし、@ KCHarwoodが言及している警告を参照してくださいApple POST要求(iOS 6以降で修正されています) 。
@ChrisopherPickslayが指摘しているように、これは全体的なタイムアウトではなく、データの受信(または送信)間のタイムアウトです。全体的なタイムアウトを賢明に行う方法を知りません。 Apple setTimeoutIntervalのドキュメントには次のように書かれています:
秒単位のタイムアウト間隔。接続試行中に、要求がタイムアウト間隔よりも長い間アイドル状態のままである場合、要求はタイムアウトしたと見なされます。デフォルトのタイムアウト間隔は60秒です。
RequestSerializer setTimeoutIntervalメソッドを使用してタイムアウト間隔を設定できます。AFHTTPRequestOperationManagerインスタンスからrequestSerializerを取得できます。
たとえば、25秒のタイムアウトでpostリクエストを実行するには:
NSDictionary *params = @{@"par1": @"value1",
@"par2": @"value2"};
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager.requestSerializer setTimeoutInterval:25]; //Time out after 25 seconds
[manager POST:@"URL" parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
//Success call back bock
NSLog(@"Request completed with response: %@", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
//Failure callback block. This block may be called due to time out or any other failure reason
}];
現時点では、手動でパッチを適用する必要があると思います。
AFHTTPClientをサブクラス化し、変更しました
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
追加する方法
[request setTimeoutInterval:10.0];
in AFHTTPClient.m line236。もちろんそれを設定できれば良いのですが、私が知る限り、現時点では不可能です。
最終的に判明非同期でそれを行う方法POSTリクエスト:
_- (void)timeout:(NSDictionary*)dict {
NDLog(@"timeout");
AFHTTPRequestOperation *operation = [dict objectForKey:@"operation"];
if (operation) {
[operation cancel];
}
[[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount];
[self perform:[[dict objectForKey:@"selector"] pointerValue] on:[dict objectForKey:@"object"] with:nil];
}
- (void)perform:(SEL)selector on:(id)target with:(id)object {
if (target && [target respondsToSelector:selector]) {
[target performSelector:selector withObject:object];
}
}
- (void)doStuffAndNotifyObject:(id)object withSelector:(SEL)selector {
// AFHTTPRequestOperation asynchronous with selector
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
@"doStuff", @"task",
nil];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL URLWithString:baseURL]];
NSMutableURLRequest *request = [httpClient requestWithMethod:@"POST" path:requestURL parameters:params];
[httpClient release];
AFHTTPRequestOperation *operation = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
operation, @"operation",
object, @"object",
[NSValue valueWithPointer:selector], @"selector",
nil];
[self performSelector:@selector(timeout:) withObject:dict afterDelay:timeout];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(timeout:) object:dict];
[[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount];
[self perform:selector on:object with:[operation responseString]];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NDLog(@"fail! \nerror: %@", [error localizedDescription]);
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(timeout:) object:dict];
[[AFNetworkActivityIndicatorManager sharedManager] decrementActivityCount];
[self perform:selector on:object with:nil];
}];
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
[[AFNetworkActivityIndicatorManager sharedManager] incrementActivityCount];
[queue addOperation:operation];
}
_
サーバーをsleep(aFewSeconds)
にして、このコードをテストしました。
同期POSTリクエストを行う必要がある場合は、[〜#〜] not [〜#〜] use _[queue waitUntilAllOperationsAreFinished];
_を使用してください。代わりに同じアプローチを使用してください非同期リクエストに関しては、セレクター引数で渡す関数がトリガーされるのを待ちます。
他のユーザーの回答と、関連するプロジェクトの問題に関する@matttの提案に基づいて、AFHTTPClient
をサブクラス化する場合の簡単な説明を次に示します。
@implementation SomeAPIClient // subclass of AFHTTPClient
// ...
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {
NSMutableURLRequest *request = [super requestWithMethod:method path:path parameters:parameters];
[request setTimeoutInterval:120];
return request;
}
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block {
NSMutableURLRequest *request = [super requestWithMethod:method path:path parameters:parameters];
[request setTimeoutInterval:120];
return request;
}
@end
IOS 6で動作するようにテスト済み。
このようなタイマーでこれを行うことはできません:
.hファイル内
{
NSInteger time;
AFJSONRequestOperation *operation;
}
.mファイル内
-(void)AFNetworkingmethod{
time = 0;
NSTtimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(startTimer:) userInfo:nil repeats:YES];
[timer fire];
operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
[self operationDidFinishLoading:JSON];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
[self operationDidFailWithError:error];
}];
[operation setJSONReadingOptions:NSJSONReadingMutableContainers];
[operation start];
}
-(void)startTimer:(NSTimer *)someTimer{
if (time == 15&&![operation isFinished]) {
time = 0;
[operation invalidate];
[operation cancel];
NSLog(@"Timeout");
return;
}
++time;
}
ここでの「タイムアウト」の定義には2つの異なる意味があります。
timeoutInterval
のようなタイムアウト任意の時間間隔よりも長くアイドル状態になった(転送がなくなった)要求をドロップします。例:timeoutInterval
を10秒に設定し、リクエストを12:00:00に開始し、12:00:23までデータを転送する場合があります。その後、接続は12:00:33にタイムアウトします。このケースは、ここでのほぼすべての回答(JosephH、Mostafa Abdellateef、Cornelius、およびGurpartap Singhを含む)でカバーされています。
timeoutDeadline
のようなタイムアウト後で任意に発生する期限に達したときに、要求をドロップします。例:deadline
を将来10秒に設定し、リクエストを12:00:00に開始すると、12:00:23までデータを転送しようとする場合がありますが、接続は12に早くタイムアウトします。 00:10。このケースは、borisdiakurによってカバーされています。
これを実装する方法を示したいdeadline in Swift(3 and 4)for AFNetworking 3.1。
let sessionManager = AFHTTPSessionManager(baseURL: baseURL)
let request = sessionManager.post(endPoint, parameters: parameters, progress: { ... }, success: { ... }, failure: { ... })
// timeout deadline at 10 seconds in the future
DispatchQueue.global().asyncAfter(deadline: .now() + 10.0) {
request?.cancel()
}
そして、テスト可能な例を示すために、このコードは将来の0.0秒での即時タイムアウトのため、「成功」ではなく「失敗」を出力する必要があります。
let sessionManager = AFHTTPSessionManager(baseURL: URL(string: "https://example.com"))
sessionManager.responseSerializer = AFHTTPResponseSerializer()
let request = sessionManager.get("/", parameters: nil, progress: nil, success: { _ in
print("success")
}, failure: { _ in
print("failure")
})
// timeout deadline at 0 seconds in the future
DispatchQueue.global().asyncAfter(deadline: .now() + 0.0) {
request?.cancel()
}