Pregunta

Mi proyecto está utilizando AfNetworking.

https://github.com/afnetworking/afnetworking

¿Cómo reduzco el tiempo de espera? ATM sin conexión a Internet El bloqueo no se activa por lo que parece alrededor de 2 minutos. Waay a largo ...

¿Fue útil?

Solución

Cambiar el intervalo de tiempo de espera es casi seguro que 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 que la red se vuelva inalcanzable, ¿no?

AFHTTPClient ya tiene un mecanismo incorporado para informarle cuándo se pierde la conexión a Internet, -setReachabilityStatusChangeBlock:.

Las solicitudes pueden llevar mucho tiempo en redes lentas. Es mejor confiar en que iOS sepa cómo tratar conexiones lentas y decir la diferencia 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:

  • Las solicitudes se pueden cancelar antes de que se inicien. Encarear una solicitud no garantiza cuándo comienza.
  • Los intervalos de tiempo de espera no deberían cancelar las solicitudes de larga duración, especialmente la publicación. Imagínese si intentara descargar o cargar un video de 100 MB. Si la solicitud avanza lo mejor que puede en una red 3G lenta, ¿por qué la detendría innecesariamente si tarda un poco más de lo esperado?
  • Haciendo performSelector:afterDelay:... puede ser peligroso en aplicaciones de múltiples subprocesos. Esto se abre a condiciones de carrera oscuras y difíciles de debilitar.

Otros consejos

Recomiendo ver la respuesta de Mattt anterior, aunque esta respuesta no cae en la falta 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, luego la solicitud de solicitud de LEGO describe una forma de hacer esto como uno de los comentarios, solo lo hace:

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 Apple no permite que esto se cambie para las solicitudes de publicación (que se soluciona en iOS 6 y hacia arriba).

Como señala @chrisopherpickslay, este no es un tiempo de espera general, es un tiempo de espera entre recibir (o enviar datos). No conozco ninguna forma de hacer un tiempo de espera general con sensatez. La documentación de Apple para SetTimeOutInterval dice:

El intervalo de tiempo de espera, en segundos. Si durante un intento de conexión, la solicitud permanece inactiva por más tiempo que el intervalo de tiempo de espera, se considera que la solicitud ha aumentado el tiempo de expulsión. El intervalo de tiempo de espera predeterminado es de 60 segundos.

Puede establecer el intervalo de tiempo de espera a través del método SolicitSerializer SetTimeOutInterval. Puede obtener el SolicitSerializer 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
    }];

Creo que tienes que parchear eso 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 hasta donde veo, eso no es posible en este momento.

Finalmente descubierto Cómo hacerlo con una solicitud de publicación 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 mi servidor sleep(aFewSeconds).

Si necesita hacer una solicitud de publicación síncrona, haga NO usar [queue waitUntilAllOperationsAreFinished];. En su lugar, use el mismo enfoque que para la solicitud asíncrona y espere a que se active la función que transmite en el argumento del selector.

Basado en las respuestas de otros y la sugerencia de @Mattt sobre temas relacionados de proyectos, aquí hay un Quickie de entrega 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 trabajar en iOS 6.

¿No podemos hacer esto con un temporizador como este?

En el archivo .h

{
NSInteger time;
AFJSONRequestOperation *operation;
}

En el 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;
}

Hay dos significados diferentes en la definición de "tiempo de espera" aquí.

Tiempo de espera como en timeoutInterval

Desea eliminar una solicitud cuando quede inactivo (no más transferencia) por más tiempo que un intervalo de tiempo arbitrario. Ejemplo: establece timeoutInterval A 10 segundos, comience su solicitud a las 12:00:00, puede transferir algunos datos hasta las 12:00:23, luego la conexión tiempo tiempo 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 eliminar una solicitud cuando llegue a una fecha límite que ocurra arbitraria más tarde. Ejemplo: establece deadline A 10 segundos en el futuro, comience su solicitud a las 12:00:00, puede intentar transferir algunos datos hasta las 12:00:23, pero la conexión se criará antes a las 12:00:10. Este caso está cubierto por Borisdiakur.

Me gustaría mostrar cómo implementar esto plazo 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 debe imprimir "falla" en lugar de "éxito" debido al tiempo de espera inmediato en 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()
}

De acuerdo con Matt, no debe intentar cambiar el TimeOutInterval. Pero tampoco debe confiar en la verificación de accesibilidad para decidir el clima que va a hacer la conexión, no lo sabe hasta que lo intente.

Según lo indicado por el documento de Apple:

Como regla general, no debe usar intervalos de tiempo de espera cortos y, en cambio, debe proporcionar una manera fácil para que el usuario cancele una operación de larga duración. Para obtener más información, lea "Diseño para redes del mundo real".

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top