Mein Projekt verwendet AFNetworking.
https://github.com/AFNetworking/AFNetworking
Wie wähle ich das Timeout herunter? Atm ohne Internetverbindung Der Fail-Block wird für ca. 2 Minuten nicht ausgelöst. Warte zu lange ....
Mein Projekt verwendet AFNetworking.
https://github.com/AFNetworking/AFNetworking
Wie wähle ich das Timeout herunter? Atm ohne Internetverbindung Der Fail-Block wird für ca. 2 Minuten nicht ausgelöst. Warte zu lange ....
Antworten:
Das Ändern des Timeout-Intervalls ist mit ziemlicher Sicherheit nicht die beste Lösung für das von Ihnen beschriebene Problem. Stattdessen scheint es so, als ob der HTTP-Client das Netzwerk nicht mehr erreichen kann, oder?
AFHTTPClient
hat bereits einen eingebauten Mechanismus, der Sie informiert, wenn die Internetverbindung unterbrochen wird -setReachabilityStatusChangeBlock:
.
Anfragen können in langsamen Netzwerken lange dauern. Es ist besser, iOS zu vertrauen, um zu wissen, wie man mit langsamen Verbindungen umgeht, und den Unterschied zwischen dem und dem Fehlen einer Verbindung zu erkennen.
Um meine Überlegungen zu erweitern, warum andere in diesem Thread erwähnte Ansätze vermieden werden sollten, hier einige Gedanken:
performSelector:afterDelay:...
kann in Multi-Threaded - Anwendungen gefährlich sein. Dies öffnet sich für obskure und schwer zu debuggende Rennbedingungen.Ich empfehle dringend, sich die Antwort von mattt oben anzuschauen - obwohl diese Antwort nicht den Problemen widerspricht, die er im Allgemeinen erwähnt, ist es für die Frage nach den Originalplakaten viel besser, die Erreichbarkeit zu überprüfen.
Wenn Sie jedoch dennoch eine Zeitüberschreitung festlegen möchten (ohne alle damit verbundenen Probleme performSelector:afterDelay:
usw.), beschreibt die von Lego erwähnte Pull-Anforderung eine Möglichkeit, dies als einen der Kommentare zu tun. Sie tun dies einfach:
NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil];
[request setTimeoutInterval:120];
AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}];
[client enqueueHTTPRequestOperation:operation];
Beachten Sie jedoch, dass @KCHarwood erwähnt, dass Apple anscheinend nicht zulässt, dass dies für POST-Anforderungen geändert wird (was in iOS 6 und höher behoben ist).
Wie @ChrisopherPickslay hervorhebt, handelt es sich nicht um eine allgemeine Zeitüberschreitung, sondern um eine Zeitüberschreitung zwischen dem Empfangen (oder Senden von Daten). Mir ist keine Möglichkeit bekannt, eine allgemeine Zeitüberschreitung sinnvoll durchzuführen. In der Apple-Dokumentation zu setTimeoutInterval heißt es:
Das Timeout-Intervall in Sekunden. Wenn die Anforderung während eines Verbindungsversuchs länger als das Zeitlimitintervall inaktiv bleibt, wird davon ausgegangen, dass die Anforderung abgelaufen ist. Das Standardzeitlimit beträgt 60 Sekunden.
timeoutInterval
ist ein Leerlaufzeitgeber, kein Anforderungszeitlimit. Sie müssten also 120 Sekunden lang überhaupt keine Daten empfangen, damit der oben genannte Code abgelaufen ist. Wenn Daten langsam eingehen, kann die Anforderung unbegrenzt fortgesetzt werden.
Sie können das Zeitlimitintervall über die setTimeoutInterval-Methode von requestSerializer festlegen. Sie können den requestSerializer von einer AFHTTPRequestOperationManager-Instanz abrufen.
Zum Beispiel, um eine Post-Anfrage mit einer Zeitüberschreitung von 25 Sekunden durchzuführen:
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
}];
Ich denke, Sie müssen das im Moment manuell patchen.
Ich bin Unterklasse AFHTTPClient und habe die geändert
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
Methode durch Hinzufügen
[request setTimeoutInterval:10.0];
in AFHTTPClient.m Zeile 236. Natürlich wäre es gut, wenn das konfiguriert werden könnte, aber soweit ich das sehe, ist das momentan nicht möglich.
Endlich herausgefunden, wie es mit einer asynchronen POST-Anfrage geht:
- (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];
}
Ich habe diesen Code getestet, indem ich meinen Server gelassen habe sleep(aFewSeconds)
.
Wenn Sie eine synchrone POST - Anforderung tun müssen, tun NICHT verwenden [queue waitUntilAllOperationsAreFinished];
. Verwenden Sie stattdessen den gleichen Ansatz wie für die asynchrone Anforderung und warten Sie, bis die Funktion ausgelöst wird, die Sie im Selektorargument weitergeben.
Basierend auf den Antworten anderer und dem Vorschlag von @ mattt zu verwandten Projektproblemen finden Sie hier einen kurzen Überblick, wenn Sie eine Unterklasse bilden 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
Getestet für iOS 6.
Können wir das nicht mit einem Timer wie diesem machen:
In der .h-Datei
{
NSInteger time;
AFJSONRequestOperation *operation;
}
In der .m-Datei
-(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;
}
Die Definition des "Timeouts" hat hier zwei verschiedene Bedeutungen.
timeoutInterval
Sie möchten eine Anforderung löschen, wenn sie länger als ein beliebiges Zeitintervall inaktiv ist (keine Übertragung mehr). Beispiel: timeoutInterval
Wenn Sie 10 Sekunden einstellen , starten Sie Ihre Anfrage um 12:00:00 Uhr. Möglicherweise werden einige Daten bis 12:00:23 Uhr übertragen. Die Verbindung wird dann um 12:00:33 Uhr unterbrochen. Dieser Fall wird hier von fast allen Antworten abgedeckt (einschließlich Joseph H., Mostafa Abdellateef, Cornelius und Gurpartap Singh).
timeoutDeadline
Sie möchten eine Anfrage löschen, wenn sie eine Frist erreicht, die später willkürlich eintritt. Beispiel: deadline
Wenn Sie in Zukunft 10 Sekunden festlegen , starten Sie Ihre Anforderung um 12:00:00 Uhr. Möglicherweise wird versucht, einige Daten bis 12:00:23 Uhr zu übertragen. Die Verbindung wird jedoch früher um 12:00:10 Uhr unterbrochen. Dieser Fall wird von borisdiakur abgedeckt.
Ich möchte zeigen, wie diese Frist in Swift (3 und 4) für AFNetworking 3.1 implementiert wird.
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()
}
Und um ein testbares Beispiel zu geben, sollte dieser Code "Fehler" anstelle von "Erfolg" ausgeben, da in Zukunft eine sofortige Zeitüberschreitung von 0,0 Sekunden auftritt:
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()
}
Stimmen Sie mit Matt überein, Sie sollten nicht versuchen, das TimeoutInterval zu ändern. Sie sollten sich aber auch nicht auf die Erreichbarkeitsprüfung verlassen, um zu entscheiden, ob Sie die Verbindung herstellen möchten. Sie wissen es erst, wenn Sie es versuchen.
Wie im Apple-Dokument angegeben:
In der Regel sollten Sie keine kurzen Zeitüberschreitungsintervalle verwenden, sondern dem Benutzer eine einfache Möglichkeit bieten, einen Vorgang mit langer Laufzeit abzubrechen. Weitere Informationen finden Sie unter „Entwerfen für reale Netzwerke“.
performSelector:afterDelay:...
vorhandene Vorgänge manuell abgebrochen werden. Bitte sehen Sie meine Antwort für weitere Details.