У моєму проекті використовується 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 секунд, щоб зазначений час закінчився. Якщо дані повільно надходять, запит може тривати нескінченно довго.
Ви можете встановити інтервал очікування через метод requestSerializer setTimeoutInterval. Ви можете отримати 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. Цей випадок охоплюється майже всіма відповідями тут (включаючи Джозефа Х., Мостафу Абдельєтеєфа, Корнеліуса та Гурпартапа Сінгха).
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()
}
Погодьтеся з Меттом, не варто намагатися змінити інтервал тайм-ауту. Але вам також не слід покладатися на перевірку доступності, щоб визначити, чи плануєте ви встановити з'єднання, ви не знаєте, поки не спробуєте.
Як зазначено в документі Apple:
Як правило, ви не повинні використовувати короткі інтервали очікування, а натомість повинні надавати користувачеві простий спосіб скасувати тривалу операцію. Для отримання додаткової інформації прочитайте “Проектування для реальних мереж”.
performSelector:afterDelay:...
для ручного скасування існуючих операцій. Будь ласка, перегляньте мою відповідь, щоб дізнатися більше