Как установить тайм-аут с AFNetworking - PullRequest
79 голосов
/ 29 ноября 2011

Мой проект использует AFNetworking.

https://github.com/AFNetworking/AFNetworking

Как мне установить тайм-аут?Банкомат без подключения к Интернету блок сбоя не срабатывает примерно 2 минуты.Вау, чтобы долго ....

Ответы [ 9 ]

109 голосов
/ 09 апреля 2012

Изменение интервала ожидания почти наверняка не лучшее решение проблемы, которую вы описываете. Вместо этого кажется, что вы действительно хотите, чтобы HTTP-клиент обрабатывал сеть, становящуюся недоступной, не так ли?

AFHTTPClient уже имеет встроенный механизм оповещения о потере подключения к Интернету, -setReachabilityStatusChangeBlock:.

Запросы могут занимать много времени в медленных сетях. Лучше довериться iOS, чтобы знать, как обрабатывать медленные соединения, и определить разницу между этим и отсутствием соединения вообще.


Чтобы расширить мои рассуждения о том, почему следует избегать других подходов, упомянутых в этой теме, вот несколько соображений:

  • Запросы можно отменить еще до их начала. Постановка запроса не дает никаких гарантий относительно того, когда он действительно начинается.
  • Интервалы времени ожидания не должны отменять долго выполняющиеся запросы, особенно POST. Представьте, что вы пытаетесь загрузить или загрузить видео размером 100 МБ. Если запрос выполняется наилучшим образом в медленной сети 3G, зачем вам его останавливать, если он занимает немного больше времени, чем ожидалось?
  • Выполнение performSelector:afterDelay:... может быть опасным в многопоточных приложениях. Это открывает себя для непонятных и трудных для отладки условий гонки.
43 голосов
/ 03 января 2012

Я настоятельно рекомендую посмотреть ответ Мэтта выше - хотя этот ответ не противоречит проблемам, которые он упоминает в целом, для оригинального вопроса об афишах проверка достижимости гораздо лучше.

Однако,если вы все еще хотите установить тайм-аут (без всех проблем, присущих 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, это неt общий тайм-аут, это тайм-аут между получением (или отправкой данных). Я не знаю ни одного способа разумно сделать общий тайм-аут. Документация Apple для setTimeoutInterval гласит:

Интервал времени ожидания, в секундах. Если во время попытки подключения запрос остается бездействующим дольше интервала ожидания, запроссчитается истекшим.Интервал ожидания по умолчанию составляет 60 секунд.

26 голосов
/ 17 июля 2014

Вы можете установить интервал ожидания с помощью метода 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
    }];
7 голосов
/ 08 января 2012

Наконец-то выяснил как это сделать с помощью асинхронного 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];.Вместо этого используйте тот же подход, что и для асинхронного запроса, и дождитесь запуска функции, которую вы передаете в аргументе селектора.

7 голосов
/ 29 ноября 2011

Я думаю, вы должны исправить это вручную на данный момент.

Я создаю подкласс AFHTTPClient и изменил

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters

метод путем добавления

[request setTimeoutInterval:10.0];

in AFHTTPClient.m Строка 236. Конечно, было бы хорошо, если бы это можно было настроить, но, насколько я понимаю, в настоящий момент это невозможно.

5 голосов
/ 14 марта 2013

Основываясь на ответах других и предложении @ 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.

0 голосов
/ 08 июня 2017

Здесь есть два разных значения определения "тайм-аут".

Тайм-аут, как в timeoutInterval

Вы хотите отбросить запрос, когда он становится бездействующим (больше не передается) дольше, чем произвольный интервал времени. Пример: вы устанавливаете timeoutInterval на 10 секунд, вы запускаете запрос в 12:00:00, некоторые данные могут быть переданы до 12:00:23, затем соединение прекратит работу в 12:00:33. Этот случай покрыт почти всеми ответами здесь (включая Иосифа, Мостафу Абделлатеефа, Корнелиуса и Гурпартарпа Сингха).

Тайм-аут, как в timeoutDeadline

Вы хотите отбросить запрос, когда он достигает крайнего срока, который произойдет произвольно позже. Пример: вы установите deadline на 10 секунд в будущем, вы начнете свой запрос в 12:00:00, он может попытаться передать некоторые данные до 12:00:23, но соединение прекратит работу раньше, в 12:00:10 , Этот случай покрыт borisdiakur.

Я хотел бы показать, как реализовать этот крайний срок в 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()
}
0 голосов
/ 19 августа 2014

Согласитесь с Мэттом, не пытайтесь изменить timeoutInterval. Но вы также не должны полагаться на проверку доступности, чтобы решить, когда вы собираетесь установить соединение, вы не знаете, пока не попробуете.

Как указано в документе Apple:

Как правило, вы не должны использовать короткие интервалы времени ожидания, а вместо этого должны предоставить пользователю простой способ отменить длительную операцию. Для получения дополнительной информации прочитайте «Проектирование для реальных сетей».

0 голосов
/ 29 сентября 2013

Разве мы не можем сделать это с таким таймером:

В файле .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;
}
...