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 ...
objective-c
ios
xcode
networking
afnetworking
jennas
źródło
źródło
performSelector:afterDelay:...
do ręcznego anulowania istniejących operacji. Zobacz moją odpowiedź, aby uzyskać więcej informacji.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?
AFHTTPClient
już 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.źródło
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:
źródło
timeoutInterval
jest 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 }];
źródło
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.
źródło
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.źródło
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.
źródło
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; }
źródło
Definicja „limitu czasu” ma tutaj dwa różne znaczenia.
Limit czasu jak w
timeoutInterval
Chcesz porzucić żądanie, gdy stanie się bezczynne (nie będzie więcej przesyłania) dłużej niż przez dowolny okres czasu. Przykład: ustawisz
timeoutInterval
10 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).Limit czasu jak w
timeoutDeadline
Chcesz odrzucić wniosek, gdy nadejdzie termin przypadający w dowolnym momencie. Przykład: ustawiłeś
deadline
na 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() }
źródło
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:
źródło