Domanda

Il mio progetto sta usando AFNetworking.

https://github.com/AFNetworking/AFNetworking

Come faccio a comporre giù il timeout? Atm senza connessione a internet il fail blocco non viene attivato per quello che si sente come a circa 2 minuti. Waay a lungo ....

È stato utile?

Soluzione

Modifica dell'intervallo di timeout non è quasi certamente la soluzione migliore al problema si sta descrivendo. Invece, sembra che quello che realmente vuole è per il client HTTP per gestire la rete di diventare irraggiungibile, no?

AFHTTPClient già ha un meccanismo incorporato per farvi sapere quando la connessione internet è perso, -setReachabilityStatusChangeBlock:.

Le richieste può richiedere molto tempo sulle reti lente. E 'meglio iOS di fiducia per sapere come trattare connessioni lente, e dire la differenza tra questo e avendo nessun collegamento a tutti.


Per espandere il mio ragionamento sul motivo per cui altri approcci citati in questa discussione dovrebbe essere evitato, qui ci sono alcuni pensieri:

  • Le richieste possono essere annullati prima di essere anche iniziato. Enqueueing una richiesta non fornisce alcuna garanzia circa quando si inizia effettivamente.
  • Gli intervalli di attesa non dovrebbero annullare le richieste, soprattutto POST di lunga durata. Immaginate se si stava tentando di scaricare o caricare un video 100MB. Se la richiesta sta andando avanti nel miglior modo possibile su una rete 3G lenta, perché si inutilmente fermarlo se si sta prendendo un po 'più tempo del previsto?
  • Fare performSelector:afterDelay:... può essere pericoloso in applicazioni multi-threaded. Questo apre se stessi fino ad oscurare e difficili da eseguire il debug condizioni di gara.

Altri suggerimenti

vi consiglio vivamente di guardare la risposta di mattt sopra -. Anche se questa risposta non rientra nel divieto di problemi egli cita, in generale, per la domanda manifesti originali, controllando raggiungibilità è una misura molto meglio

Tuttavia, se si vuole ancora impostare un timeout (senza tutti i problemi inerenti performSelector:afterDelay: ecc, allora la richiesta di pull Lego menziona descrive un modo per fare questo come uno dei commenti, basta fare:

NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil];
[request setTimeoutInterval:120];

AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}];
[client enqueueHTTPRequestOperation:operation];

, ma vedere il @KCHarwood avvertenza menziona il fatto che sembra di Apple non permettere che questo essere cambiato per le richieste POST (che è fissato in iOS 6 e verso l'alto).

Come @ChrisopherPickslay fa notare, questo non è un timeout nel complesso, è un timeout tra la ricezione (o invio di dati). Io non sono a conoscenza di un modo per fare un timeout sensibilmente complessiva. La documentazione Apple per setTimeoutInterval dice:

L'intervallo di timeout, in secondi. Se durante una connessione tentare la richiesta rimane inattiva per oltre l'intervallo di timeout, la richiesta è considerato di avere fuori a tempo. L'intervallo di timeout predefinito è 60 secondi.

È possibile impostare l'intervallo di timeout attraverso requestSerializer setTimeoutInterval method.You può ottenere il requestSerializer da un'istanza AFHTTPRequestOperationManager.

Per esempio, per fare una richiesta POST con un timeout di 25 secondi:

    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
    }];

Credo che bisogna applicare una patch che manualmente al momento.

Io sono sottoclassi AFHTTPClient e ha cambiato il

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters

metodo aggiungendo

[request setTimeoutInterval:10.0];

AFHTTPClient.m linea 236. Naturalmente sarebbe bello se questo potrebbe essere configurato, ma per quanto mi accorgo che non è possibile al momento.

Infine scoperto come farlo con una richiesta POST asincrona:

- (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];
}

Ho provato questo codice, lasciando il mio sleep(aFewSeconds) server.

Se avete bisogno di fare una richiesta POST sincrona, fare non uso [queue waitUntilAllOperationsAreFinished];. Invece di utilizzare lo stesso approccio per la richiesta asincrona e attendere che la funzione da attivare, che si passa sul nell'argomento di selezione.

Sulla base delle risposte degli altri e @ suggerimento di mattt su questioni progetto legato, ecco un drop-in sveltina se siete sottoclasse 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

testato per funzionare su iOS 6.

Non possiamo fare questo con un timer in questo modo:

Nel file .h

{
NSInteger time;
AFJSONRequestOperation *operation;
}

Nel file di .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;
}

Ci sono due significati differenti sulla definizione "timeout" qui.

Timeout come in timeoutInterval

si vuole far cadere una richiesta quando diventa inattiva (non più di trasferimento) per più di un intervallo di tempo arbitrario. Esempio: si imposta timeoutInterval a 10 secondi, si avvia la richiesta alle 12:00:00, esso può trasferire alcuni dati fino 00:00:23, allora il collegamento verrà timeout alle 12:00:33. Questo caso è coperto da quasi tutte le risposte qui (tra cui JosephH, Mostafa Abdellateef, Cornelio e Gurpartap Singh).

Timeout come in timeoutDeadline

si vuole far cadere una richiesta quando raggiunge una scadenza arbitraria accadendo in seguito. Esempio: si imposta deadline a 10 secondi, in futuro, si avvia la richiesta alle 12:00:00, può tentare di trasferire alcuni dati fino 00:00:23, ma la connessione scadrà prima a 00:00:10. Questo caso è coperto da borisdiakur.

Mi piacerebbe mostrare come implementare questa scadenza a Swift (3 e 4) per 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()
}

E per fare un esempio verificabile, questo codice dovrebbe stampare "fallimento" al posto di "successo" a causa del timeout immediato a 0,0 secondi in 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()
}

D'accordo con Matt, non si dovrebbe provare a cambiare il TimeoutInterval. Ma anche voi non dovrebbe fare affidamento su di controllo raggiungibilità per decidere tempo si sta andando effettuare la connessione, non si sa fino a quando si tenta.

Come indicato dal documento di Apple:

Come regola generale, non si dovrebbe usare intervalli di timeout brevi, e, invece, dovrebbe fornire un modo semplice per l'utente di annullare un'operazione di lunga esecuzione. Per ulteriori informazioni, leggere “Progettare per le reti del mondo reale”.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top