Mi proyecto utiliza AFNetworking.
https://github.com/AFNetworking/AFNetworking
¿Cómo reduzco el tiempo de espera? Cajero automático sin conexión a Internet, el bloqueo de fallas no se activa durante lo que se siente como aproximadamente 2 minutos. Mucho tiempo ...
objective-c
ios
xcode
networking
afnetworking
Jennas
fuente
fuente
performSelector:afterDelay:...
para cancelar manualmente las operaciones existentes. Consulte mi respuesta para obtener más detalles.Respuestas:
Es casi seguro que cambiar el intervalo de tiempo de espera no es la mejor solución al problema que está describiendo. En cambio, parece que lo que realmente desea es que el cliente HTTP maneje la red que se vuelve inalcanzable, ¿no?
AFHTTPClient
ya tiene un mecanismo incorporado para avisarle cuando se pierde la conexión a Internet,-setReachabilityStatusChangeBlock:
.Las solicitudes pueden tardar bastante en redes lentas. Es mejor confiar en iOS para saber cómo manejar conexiones lentas y diferenciar entre eso y no tener ninguna conexión.
Para ampliar mi razonamiento sobre por qué se deben evitar otros enfoques mencionados en este hilo, aquí hay algunos pensamientos:
performSelector:afterDelay:...
puede ser peligroso en aplicaciones de subprocesos múltiples. Esto se abre a condiciones de carrera oscuras y difíciles de depurar.fuente
Recomiendo encarecidamente mirar la respuesta de mattt anterior, aunque esta respuesta no cae en contra de los problemas que menciona en general, para la pregunta de los carteles originales, verificar la accesibilidad es mucho mejor.
Sin embargo, si aún desea establecer un tiempo de espera (sin todos los problemas inherentes a
performSelector:afterDelay:
, etc., entonces la solicitud de extracción que Lego menciona describe una forma de hacerlo como uno de los comentarios, simplemente haga:NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil]; [request setTimeoutInterval:120]; AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}]; [client enqueueHTTPRequestOperation:operation];
pero vea la advertencia @KCHarwood menciona que parece que Apple no permite que esto se cambie para las solicitudes POST (que se corrigió en iOS 6 y versiones posteriores).
Como señala @ChrisopherPickslay, este no es un tiempo de espera general, es un tiempo de espera entre la recepción (o el envío de datos). No tengo conocimiento de ninguna forma sensata de hacer un tiempo de espera general. La documentación de Apple para setTimeoutInterval dice:
fuente
timeoutInterval
es un temporizador inactivo, no un tiempo de espera de solicitud. Por lo tanto, no tendría que recibir ningún dato durante 120 segundos para que el código anterior expire. Si los datos se filtran lentamente, la solicitud podría continuar indefinidamente.Puede establecer el intervalo de tiempo de espera a través del método requestSerializer setTimeoutInterval. Puede obtener el requestSerializer de una instancia AFHTTPRequestOperationManager.
Por ejemplo, para hacer una solicitud de publicación con un tiempo de espera de 25 segundos:
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 }];
fuente
Creo que tienes que parchear eso manualmente en este momento.
Estoy subclasificando AFHTTPClient y cambié el
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
método agregando
[request setTimeoutInterval:10.0];
en AFHTTPClient.m línea 236. Por supuesto, sería bueno si eso pudiera configurarse, pero por lo que veo, eso no es posible en este momento.
fuente
Finalmente descubrí cómo hacerlo con una solicitud POST asincrónica:
- (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]; }
Probé este código dejando que mi servidor
sleep(aFewSeconds)
.Si necesita hacer una solicitud POST sincrónica, NO use
[queue waitUntilAllOperationsAreFinished];
. En su lugar, use el mismo enfoque que para la solicitud asincrónica y espere a que se active la función que pasa en el argumento del selector.fuente
Según las respuestas de otros y la sugerencia de @ mattt sobre problemas relacionados con el proyecto, aquí hay un resumen rápido si está subclasificando
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
Probado para funcionar en iOS 6.
fuente
¿No podemos hacer esto con un temporizador como este?
En archivo .h
{ NSInteger time; AFJSONRequestOperation *operation; }
En archivo .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; }
fuente
Hay dos significados diferentes en la definición de "tiempo de espera" aquí.
Tiempo de espera como en
timeoutInterval
Desea descartar una solicitud cuando esté inactiva (no más transferencia) durante más de un intervalo de tiempo arbitrario. Ejemplo: estableces
timeoutInterval
en 10 segundos, inicia su solicitud a las 12:00:00, puede transferir algunos datos hasta las 12:00:23, luego la conexión expirará a las 12:00:33. Este caso está cubierto por casi todas las respuestas aquí (incluidas JosephH, Mostafa Abdellateef, Cornelius y Gurpartap Singh).Tiempo de espera como en
timeoutDeadline
Desea descartar una solicitud cuando llega a una fecha límite que ocurre arbitrariamente más tarde. Ejemplo: establece
deadline
10 segundos en el futuro, inicia su solicitud a las 12:00:00, puede intentar transferir algunos datos hasta las 12:00:23, pero la conexión se agotará antes a las 12:00:10. Este caso está cubierto por borisdiakur.Me gustaría mostrar cómo implementar esta fecha límite en Swift (3 y 4) para 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() }
Y para dar un ejemplo comprobable, este código debería imprimir "falla" en lugar de "éxito" debido al tiempo de espera inmediato de 0.0 segundos en el futuro:
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() }
fuente
De acuerdo con Matt, no debería intentar cambiar el timeoutInterval. Pero tampoco debe confiar en la verificación de accesibilidad para decidir el clima en el que realizará la conexión, no lo sabrá hasta que lo intente.
Como se indica en el documento de Apple:
fuente