Mój projekt korzysta z AFNetworking.
https://github.com/AFNetworking/AFNetworking
Jak skrócić limit czasu? W przypadku braku połączenia z Internetem blokada awarii nie jest wyzwalana przez około 2 minuty. Czekaj zbyt długo ...
Mój projekt korzysta z AFNetworking.
https://github.com/AFNetworking/AFNetworking
Jak skrócić limit czasu? W przypadku braku połączenia z Internetem blokada awarii nie jest wyzwalana przez około 2 minuty. Czekaj zbyt długo ...
Odpowiedzi:
Zmiana limitu czasu prawie na pewno nie jest najlepszym rozwiązaniem opisywanego problemu. Zamiast tego wydaje się, że tak naprawdę chcesz, aby klient HTTP poradził sobie z utratą dostępu do sieci, nie?
AFHTTPClientjuż ma wbudowany mechanizm poinformować, gdy internet jest stracone, -setReachabilityStatusChangeBlock:.
Żądania mogą zająć dużo czasu w powolnych sieciach. Lepiej zaufać systemowi iOS, aby wiedzieć, jak radzić sobie z powolnymi połączeniami i odróżnić to od braku połączenia.
Aby rozwinąć moje rozumowanie, dlaczego należy unikać innych podejść wspomnianych w tym wątku, oto kilka myśli:
performSelector:afterDelay:...może być niebezpieczne w aplikacjach wielowątkowych. To otwiera się na niejasne i trudne do debugowania warunki wyścigu.Zdecydowanie polecam przyjrzenie się powyższej odpowiedzi Mattta - chociaż ta odpowiedź nie jest obarczona problemami, o których wspomina w ogóle, w przypadku oryginalnego pytania dotyczącego plakatów sprawdzenie osiągalności jest znacznie lepszym rozwiązaniem.
Jeśli jednak nadal chcesz ustawić limit czasu (bez wszystkich problemów związanych z performSelector:afterDelay:itp., Wówczas żądanie ściągnięcia Lego wspomina o sposobie zrobienia tego jako jeden z komentarzy, po prostu wykonaj:
NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil];
[request setTimeoutInterval:120];
AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}];
[client enqueueHTTPRequestOperation:operation];
ale zobacz zastrzeżenie @KCHarwood wspomina, że wygląda na to, że Apple nie zezwala na zmianę tego dla żądań POST (co jest naprawione w iOS 6 i nowszych).
Jak wskazuje @ChrisopherPickslay, nie jest to ogólny limit czasu, jest to limit czasu między otrzymaniem (lub wysłaniem danych). Nie znam żadnego sposobu na rozsądne przekroczenie limitu czasu. Dokumentacja Apple dotycząca setTimeoutInterval mówi:
Limit czasu w sekundach. Jeśli podczas próby połączenia żądanie pozostaje bezczynne przez czas dłuższy niż limit czasu, uważa się, że upłynął limit czasu żądania. Domyślny limit czasu to 60 sekund.
timeoutIntervaljest licznikiem czasu bezczynności, a nie limitem czasu żądania. Więc musiałbyś otrzymywać żadne dane przez 120 sekund, aby powyższy kod wygasł. Jeśli dane powoli napływają, żądanie może trwać w nieskończoność.
Limit czasu można ustawić za pomocą metody requestSerializer setTimeoutInterval. RequestSerializer można pobrać z instancji AFHTTPRequestOperationManager.
Na przykład, aby wysłać żądanie wpisu z limitem czasu 25 sekund:
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
}];
Myślę, że w tej chwili musisz to załatać ręcznie.
Podklasuję AFHTTPClient i zmieniłem
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
metoda poprzez dodanie
[request setTimeoutInterval:10.0];
w wierszu AFHTTPClient.m 236. Oczywiście byłoby dobrze, gdyby można to skonfigurować, ale z tego, co widzę, w tej chwili nie jest to możliwe.
Wreszcie dowiedziałem się, jak to zrobić za pomocą asynchronicznego żądania 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];
}
Przetestowałem ten kod, pozwalając mojemu serwerowi sleep(aFewSeconds).
Jeśli chcesz wykonać synchroniczne żądanie POST, NIE używaj [queue waitUntilAllOperationsAreFinished];. Zamiast tego użyj tego samego podejścia, co w przypadku żądania asynchronicznego i poczekaj na wyzwolenie funkcji, którą przekazujesz w argumencie selektora.
W oparciu o odpowiedzi innych osób i sugestię @ mattt na temat powiązanych problemów z projektem, oto szybki numerek, jeśli tworzysz podklasy 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
Przetestowano pod kątem działania na iOS 6.
Czy nie możemy tego zrobić z takim timerem:
W pliku .h
{
NSInteger time;
AFJSONRequestOperation *operation;
}
W pliku .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;
}
Definicja „limitu czasu” ma tutaj dwa różne znaczenia.
timeoutIntervalChcesz porzucić żądanie, gdy stanie się bezczynne (nie będzie więcej przesyłania) dłużej niż przez dowolny okres czasu. Przykład: ustawisz timeoutInterval10 sekund, zaczynasz żądanie o 12:00:00, może przesłać część danych do 12:00:23, a następnie połączenie wygaśnie o 12:00:33. Ten przypadek jest objęty prawie wszystkimi odpowiedziami tutaj (w tym JosephH, Mostafa Abdellateef, Cornelius i Gurpartap Singh).
timeoutDeadlineChcesz odrzucić wniosek, gdy nadejdzie termin przypadający w dowolnym momencie. Przykład: ustawiłeś deadlinena 10 sekund w przyszłości, zaczynasz żądanie o 12:00:00, może próbować przesłać niektóre dane do 12:00:23, ale połączenie wygaśnie wcześniej o 12:00:10. Ten przypadek pokrywa borisdiakur.
Chciałbym pokazać, jak wdrożyć ten termin w Swift (3 i 4) dla 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()
}
Aby dać testowalny przykład, ten kod powinien wypisać „niepowodzenie” zamiast „sukces” ze względu na natychmiastowy limit czasu wynoszący 0,0 sekundy w przyszłości:
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()
}
Zgadzam się z Mattem, nie powinieneś próbować zmieniać timeoutInterval. Ale nie powinieneś również polegać na sprawdzaniu osiągalności, aby zdecydować, czy zamierzasz nawiązać połączenie, nie wiesz, dopóki nie spróbujesz.
Jak stwierdzono w dokumencie Apple:
Zasadniczo nie należy używać krótkich przedziałów czasu, a zamiast tego należy zapewnić użytkownikowi łatwy sposób na anulowanie długotrwałej operacji. Aby uzyskać więcej informacji, przeczytaj „Projektowanie sieci w świecie rzeczywistym”.
performSelector:afterDelay:...do ręcznego anulowania istniejących operacji. Zobacz moją odpowiedź, aby uzyskać więcej informacji.