13

ほとんどではないにしても、多くのWebサービスにはクライアントのレート制限があります。Deliciousは、クライアントが1秒に1つのリクエストを行うことができると言います。Twitterにはエンドポイントごとの制限があります。FacebookとFlickrとFoursquareは独自のアイデアを持っていると確信しています。

を使用して、iOSアプリケーションを一度に1つのリクエストに簡単に制限できますNSOperationQueue

しかし、アプリケーションを、たとえば1秒に1つのリクエストのみを行うように制限するにはどうすればよいでしょうか。

Apple、AFNetworking、ASINetwork、その他数社のサンプルコードを見てきましたが、この問題を解決できるものはないようです。これは私には奇妙に思えます。私は非常に明白な何かを見逃している可能性があることを認めます...

いくつかのパラメータ:

  • NSOperationQueue私がネットワーク操作用に持っていて、要求がNSOperation(私が推測するGCDキューである可能性もありますが、これは私が主に取り組んできたものです)であると仮定します
  • キュー内の各リクエストに同じレート制限が使用されます
  • iOSで解決策を探していますが、一般的なアイデアが役立つかもしれません

可能な解決策:

  • sleepNSOperation(これはキュー/スレッドなので、他のものをブロックすることはありません)のステートメント
  • NSTimerの中にNSOperation
  • performSelector:NSOperation私はこのアプローチを使用するようにASINetworkingにパッチを適用しましたが、私はそれを使用しておらず、変更をアップストリームにプッシュしませんでした)
  • キューを開始/停止して(KVOを使用しますか?)、レート制限を超えないようにします
  • 特別な「睡眠」NSOperation。これは、次のネットワーク操作が依存するタスクになります
  • レート制限を完全に無視し、「レート制限を超えました」というエラー応答が表示されたら少し一時停止します

これらはすべてかなり厄介なようです。スリープ状態の操作は、「優先」キューの形式を妨げる可能性があります。キューの開始/停止は壊れやすいようです。制限を無視するのは失礼です。

明確にするために、私はこの問題を解決しました。しかし、解決策は「乱雑」でやや壊れやすいようです。より良い、よりクリーンなオプションがあるかどうか知りたいのですが。

アイデア?

4

4 に答える 4

6
@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];
于 2012-11-05T13:41:27.890 に答える
0

これは、Edwin が提供するほぼ機能するソリューションの修正です。

- (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
    });
}
于 2019-10-11T12:10:55.157 に答える