Как оценить предел моих сетевых запросов IOS на одну секунду

Многие, если не большинство, веб-службы имеют ограничение скорости для клиентов. Delicious говорит, что клиент может сделать один запрос в секунду; У Twitter есть ограничения на конечную точку; Я уверен, что у Facebook и Flickr и Foursquare есть своя идея.

Вы можете легко ограничить приложение iOS одним запросом за раз, используя NSOperationQueue.

Но как ограничить приложение созданием, скажем, только одного запроса в секунду?

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

Некоторые параметры:

  • Предположим, что у меня есть NSOperationQueue для сетевых операций, а запрос - NSOperation (также может быть очередь GCD, я полагаю, но это то, с чем я в основном работал)
  • Тот же предел скорости используется для каждого запроса в очереди
  • Я ищу решение в iOS, но общие идеи могут быть полезны

Возможные решения:

  • sleep в NSOperation (это очередь/поток, поэтому это ничего не блокирует)
  • NSTimer в NSOperation
  • performSelector: в NSOperation (Я исправил ASINetworking, чтобы использовать этот подход, хотя я не использую его и не использовал нажмите изменение вверх по течению)
  • Запустить/остановить очередь (используя KVO?), чтобы убедиться, что предел скорости не превышен.
  • Специальный "сон" NSOperation. Это будет задачей, согласно которой следующая операция сети будет зависеть от
  • Полностью проигнорировать ограничение скорости и немного приостановить, когда вы получите сообщение об ошибке "превышение скорости"

Все это кажется довольно грязным. Операции, которые спят, вероятно, будут препятствовать формам очереди "приоритет". Запуск/остановка очереди кажется хрупкой. Игнорирование ограничения является грубым.

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

Идеи?

Ответ 1

@implementation SomeNSOperationSubClass {
    BOOL complete;
    BOOL stopRunLoop;
    NSThread *myThread;
}

-(void) rateLimitMonitor:(NSTimer *)theTimer {
    [theTimer invalidate];
}

-(void) main {
    myThread = [NSThread currentThread];

    NSTimer *myTimer = [NSTimer timerWithTimeInterval:1 target:self  selector:@selector(rateLimitMonitor:) userInfo:nil repeats:NO];
    [[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSDefaultRunLoopMode];

    [self doAsyncThing];

    while ((!stopRunLoop || [myTimer isValid]) && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
    complete = YES;
}

-(void) internalComplete {
    stopRunLoop = YES;
}

-(void) setComplete {
    [self performSelector:@selector(internalComplete) onThread:myThread withObject:nil waitUntilDone:NO];
}

-(BOOL) isFinished {
    return complete;
}

@end

и в вашем асинхронном обратном вызове

    [myNSOperationSubClass setComplete];

Ответ 2

Это исправление для почти работающего решения, предоставленного Эдвином

- (void)main {
    for (double delay = 0.0; delay < 10.0; delay+=1) {
        [self networkCallWithDelay:delay];
    }
}

- (void)networkCallWithDelay:(double)delay {

      dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

    // Your asynchronous network call goes here
    });
}

Ответ 3

НЕ ОГРАНИЧИТЕЛЬНОЕ РЕШЕНИЕ, НО ЗАКРЫТЬ

Мне нужно было оценить ограничение моего запроса, чтобы избежать ответа 429, вызванного слишком большим количеством запросов.

Возможное решение, если вы уже внедрили свой сетевой модуль, используя другие технологии, кроме NSOperation.

Используйте GCD для решения этой проблемы. Следующий код вводит 1 секунду задержки для каждого сетевого вызова. Обратите особое внимание на параметр popTime

- (void)main {

        for (NSInteger index = 0; index < 10; index++) {
            [self networkCallWithDelay:1.0f];
        }
    }

//Ваш сетевой код идет здесь

- (void)networkCallWithDelay:(double)delay {

    double delayInSeconds = delay / 10.0f;

    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){

        // Your asynchronous network call goes here
    });
}