Мой проект использует AFNetworking.
https://github.com/AFNetworking/AFNetworking
Как уменьшить тайм-аут? Банкомат без подключения к Интернету блокировка сбоя не срабатывает в течение примерно 2 минут. Ооочень долго....
Мой проект использует AFNetworking.
https://github.com/AFNetworking/AFNetworking
Как уменьшить тайм-аут? Банкомат без подключения к Интернету блокировка сбоя не срабатывает в течение примерно 2 минут. Ооочень долго....
Изменение интервала ожидания почти наверняка не лучшее решение проблемы, которую вы описываете. Вместо этого кажется, что вы на самом деле хотите, чтобы HTTP-клиент обрабатывал сеть, становящуюся недоступной, не так ли?
AFHTTPClient
уже имеет встроенный механизм оповещения о потере интернет-соединения, -setReachabilityStatusChangeBlock:
.
Запросы могут занимать много времени в медленных сетях. Лучше доверять iOS, чтобы знать, как справляться с медленными соединениями, и определять разницу между этим и полным отсутствием соединения.
Чтобы расширить мои рассуждения о том, почему следует избегать других подходов, упомянутых в этой теме, вот несколько мыслей:
performSelector:afterDelay:...
может быть опасным в многопоточных приложениях. Это открывает перед вами непонятные и трудно отлаживаемые условия гонки.Я настоятельно рекомендую посмотреть ответ Мэтта выше - хотя этот ответ не противоречит проблемам, которые он упоминает в целом, для исходного вопроса плакатов проверка достижимости подходит гораздо лучше.
Однако, если вы все же хотите установить тайм-аут (без всех проблем, присущих performSelector:afterDelay:
и т. д., то запрос на вытягивание, который упоминает Lego, описывает способ сделать это как один из комментариев, вы просто делаете:
NSMutableURLRequest *request = [client requestWithMethod:@"GET" path:@"/" parameters:nil];
[request setTimeoutInterval:120];
AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request success:^{...} failure:^{...}];
[client enqueueHTTPRequestOperation:operation];
но обратите внимание на оговорку, в которой @KCHarwood упоминает, что, похоже, Apple не разрешает изменять это для запросов POST (что исправлено в iOS 6 и выше).
Как указывает @ChrisopherPickslay, это не общий тайм-аут, это тайм-аут между получением (или отправкой данных). Я не знаю, как разумно сделать общий тайм-аут. В документации Apple для setTimeoutInterval говорится:
Интервал ожидания в секундах. Если во время попытки подключения запрос остается бездействующим дольше интервала ожидания, считается, что запрос истек. Интервал ожидания по умолчанию составляет 60 секунд.
timeoutInterval
— это таймер простоя, а не время ожидания запроса. Таким образом, вам придется вообще не получать никаких данных в течение 120 секунд, чтобы приведенный выше код истек. Если данные поступают медленно, запрос может продолжаться бесконечно.
- person Christopher Pickslay; 12.11.2013
Вы можете установить интервал времени ожидания с помощью метода setTimeoutInterval requestSerializer. Вы можете получить requestSerializer из экземпляра AFHTTPRequestOperationManager.
Например, чтобы сделать почтовый запрос с тайм-аутом 25 секунд:
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
}];
Я думаю, вам нужно исправить это вручную на данный момент.
Я подклассифицирую AFHTTPClient и изменил
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters
метод путем добавления
[request setTimeoutInterval:10.0];
в строке AFHTTPClient.m 236. Конечно, было бы хорошо, если бы это можно было настроить, но, насколько я понимаю, в данный момент это невозможно.
Наконец-то выяснил, как это сделать с помощью асинхронного 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];
}
Я протестировал этот код, разрешив моему серверу sleep(aFewSeconds)
.
Если вам нужно выполнить синхронный запрос POST, НЕ используйте [queue waitUntilAllOperationsAreFinished];
. Вместо этого используйте тот же подход, что и для асинхронного запроса, и дождитесь запуска функции, которую вы передаете в аргументе селектора.
Основываясь на ответах других и предложениях @mattt по связанным с проектом вопросам, вот краткое описание, если вы создаете подкласс 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
Протестировано для работы на iOS 6.
Разве мы не можем сделать это с таким таймером:
В файле .h
{
NSInteger time;
AFJSONRequestOperation *operation;
}
В файле .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;
}
Здесь есть два разных значения определения тайм-аута.
timeoutInterval
Вы хотите отбросить запрос, когда он становится бездействующим (больше нет передачи) дольше, чем произвольный интервал времени. Пример: вы устанавливаете timeoutInterval
на 10 секунд, вы начинаете свой запрос в 12:00:00, он может передавать некоторые данные до 12:00:23, затем время ожидания соединения истекает в 12:00:33. Этот случай охвачен почти всеми ответами здесь (включая JosephH, Mostafa Abdellateef, Cornelius и Gurpartap Singh).
timeoutDeadline
Вы хотите удалить запрос, когда он достигает крайнего срока, который происходит произвольно позже. Пример: вы устанавливаете deadline
на 10 секунд в будущем, вы начинаете свой запрос в 12:00:00, он может попытаться передать некоторые данные до 12:00:23, но время ожидания соединения прервется раньше в 12:00:10. Это дело покрывает борисдиакур.
Я хотел бы показать, как реализовать этот крайний срок в Swift (3 и 4) для 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()
}
И чтобы привести пример для тестирования, этот код должен печатать отказ вместо успеха из-за немедленного тайм-аута в 0,0 секунды в будущем:
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()
}
Согласитесь с Мэттом, вам не следует пытаться изменить timeoutInterval. Но вы также не должны полагаться на проверку достижимости, чтобы решить, собираетесь ли вы установить соединение, вы не знаете, пока не попробуете.
Как указано в документе Apple:
Как правило, вы не должны использовать короткие интервалы времени ожидания, а вместо этого должны предоставить пользователю простой способ отменить длительную операцию. Для получения дополнительной информации прочитайте «Проектирование для реальных сетей».
performSelector:afterDelay:...
для ручной отмены существующих операций. Пожалуйста, смотрите мой ответ для более подробной информации. - person mattt   schedule 09.04.2012