Mon projet utilise AFNetworking.
https://github.com/AFNetworking/AFNetworking
Comment puis-je réduire le délai? Sans connexion Internet, le bloc d'échec n'est pas déclenché pour une durée d'environ 2 minutes. Waay à long ....
Changer le délai d'attente n'est certainement pas la meilleure solution au problème que vous décrivez. Au lieu de cela, il semble que ce que vous voulez réellement, c’est que le client HTTP gère le réseau qui devient inaccessible, non?
AFHTTPClient
possède déjà un mécanisme intégré vous permettant de savoir quand une connexion Internet est perdue, -setReachabilityStatusChangeBlock:
.
Les demandes peuvent prendre beaucoup de temps sur des réseaux lents. Il vaut mieux faire confiance à iOS pour savoir comment gérer les connexions lentes et faire la différence entre cela et ne pas avoir de connexion du tout.
Pour développer mon raisonnement sur la raison pour laquelle les autres approches mentionnées dans ce fil devraient être évitées, voici quelques réflexions:
performSelector:afterDelay:...
peut être dangereux dans les applications multithreads. Cela s’ouvre à des conditions de course obscures et difficiles à déboguer.Je recommande fortement de regarder la réponse de mattt ci-dessus - bien que cette réponse ne déroge pas aux problèmes qu'il mentionne en général, pour la question des affiches originales, la vérification de l'accessibilité est un bien meilleur ajustement.
Toutefois, si vous souhaitez toujours définir un délai d’expiration (sans tous les problèmes inhérents à performSelector:afterDelay:
etc, alors la demande de tirage mentionnée par Lego décrit une façon de le faire en tant que commentaire, vous faites juste:
NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil];
[request setTimeoutInterval:120];
AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}];
[client enqueueHTTPRequestOperation:operation];
mais voyez la mise en garde @KCHarwood mentionne qu'il apparaît Apple ne permet pas que cela soit changé pour POST (qui est corrigé dans iOS 6 et plus)) .
Comme @ChrisopherPickslay le souligne, il ne s'agit pas d'un délai d'attente global, mais d'un délai d'attente entre la réception (ou l'envoi de données). Je ne suis au courant d'aucun moyen de faire raisonnablement un délai d'attente global. La documentation de Apple pour setTimeoutInterval dit:
Le délai d'attente, en secondes. Si, au cours d'une tentative de connexion, la demande reste inactive plus longtemps que le délai imparti, la demande est considérée comme ayant expiré. L'intervalle de temporisation par défaut est de 60 secondes.
Vous pouvez définir l'intervalle de délai d'attente via la méthode requestSerializer setTimeoutInterval.Vous pouvez obtenir le requestSerializer à partir d'une instance AFHTTPRequestOperationManager.
Par exemple, pour faire une demande de publication avec un délai d’attente de 25 secondes:
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
}];
Je pense que vous devez corriger cela manuellement pour le moment.
Je sous-classe AFHTTPClient et ai changé le
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
méthode en ajoutant
[request setTimeoutInterval:10.0];
dans AFHTTPClient.m ligne 236. Bien sûr, il serait bon que cela puisse être configuré, mais pour autant que je sache, cela n’est pas possible pour le moment.
Enfin découvert comment le faire avec une requête asynchrone 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];
}
J'ai testé ce code en laissant mon serveur sleep(aFewSeconds)
.
Si vous devez effectuer une demande synchrone POST, faites ET NON PAS =)) == utilisez [queue waitUntilAllOperationsAreFinished];
. Utilisez plutôt le même approche comme pour la demande asynchrone et attendez le déclenchement de la fonction que vous transmettez dans l’argument du sélecteur.
Sur la base des réponses des autres et de la suggestion de @ mattt concernant des problèmes de projet connexes, voici un exemple rapide si vous sous-classez 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
Testé pour fonctionner sur iOS 6.
Ne pouvons-nous pas faire cela avec une minuterie comme celle-ci:
Dans le fichier .h
{
NSInteger time;
AFJSONRequestOperation *operation;
}
Dans le fichier .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;
}
Il existe deux sens différents sur la définition de "délai d'attente" ici.
timeoutInterval
Vous souhaitez supprimer une demande lorsqu'elle devient inactive (pas plus de transfert) pendant une durée supérieure à un intervalle de temps arbitraire. Exemple: vous définissez timeoutInterval
sur 10 secondes, vous commencez votre demande à 12h00, il est possible que certaines données soient transférées jusqu'à 12h23, puis la connexion expire à 12h33. Cette affaire est couverte par presque toutes les réponses ici (y compris JosephH, Mostafa Abdellateef, Cornelius et Gurpartap Singh).
timeoutDeadline
Vous souhaitez supprimer une demande lorsqu'elle atteint une date limite ultérieure et arbitraire. Exemple: vous définissez deadline
sur 10 secondes à l’avenir, vous lancez votre demande à 12h00, il peut tenter de transférer des données jusqu’à 12h23, mais la connexion expirera plus tôt à 12: 00h10. Cette affaire est couverte par borisdiakur.
Je voudrais montrer comment implémenter ceci date limite dans Swift (3 et 4) pour 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()
}
Et pour donner un exemple testable, ce code devrait afficher "échec" au lieu de "succès" en raison du délai d'attente immédiat à 0,0 seconde dans le futur:
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()
}