2012-11-05 5 views
11

De nombreux services Web, sinon la plupart, ont une limite de débit pour les clients. Delicious dit qu'un client peut faire une demande par seconde; Twitter a des limites par point final; Je suis sûr que Facebook et Flickr et Foursquare ont leur propre idée.Comment limiter mes demandes de réseau iOS à une seconde

Vous pouvez facilement limiter une application iOS à une seule demande à la fois en utilisant un NSOperationQueue. Mais comment limiter une application à faire, disons, une seule requête par seconde? J'ai regardé l'exemple de code par Apple, AFNetworking, ASINetwork et quelques autres, et aucun ne semble résoudre ce problème. Cela me semble étrange. Je reconnais que je ne pouvais manquer quelque chose très évident ...

Certains paramètres:

  • Supposons que j'ai un NSOperationQueue pour les opérations de réseau et la demande est un NSOperation (pourrait aussi être un GCD file d'attente je suppose, mais c'est ce que j'ai surtout travaillé avec)
  • La même limite de taux est utilisée pour chaque requête dans la file d'attente
  • Je cherche une solution dans iOS, mais des idées générales pourraient être utiles

solutions possibles:

  • déclaration sleep dans le NSOperation (c'est une file d'attente/thread donc ce ne serait pas bloquer quoi que ce soit d'autre)
  • NSTimer dans le NSOperation
  • performSelector: dans le NSOperation (I patched ASINetworking to use this approach , bien que je ne l'utilise pas et n'ai pas poussé le changement en amont)
  • Démarrer/arrêter la file d'attente (en utilisant KVO?) pour vous assurer que le taux limi t n'est pas dépassé
  • Spécial "sommeil" NSOperation. Ce serait une tâche que la prochaine opération de réseau dépendrait
  • ignorer complètement la limite de vitesse et juste une pause un peu quand vous obtenez la réponse d'erreur « limite de vitesse dépassé »

Ces tous semblent tout à fait désordre. Les opérations en veille empêcheront probablement les formes de file d'attente "prioritaires". Démarrer/arrêter la file d'attente semble fragile. Ignorer la limite est grossier.

Pour être clair, j'ai résolu ce problème. Mais la solution semble "désordonnée" et plutôt fragile. J'aimerais savoir s'il existe une meilleure option, plus propre.

Des idées?

Répondre

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 

et dans votre rappel async

[myNSOperationSubClass setComplete]; 
+0

Un grand merci pour votre aide. Cela ressemble beaucoup à ce que j'avais en tête pour mon option 2 mais puisque vous montrez exactement comment le faire et qu'il n'y a pas de points de vue divergents, je vais accepter votre réponse. –

-1

PAS UN TAUX DE LIMITER LA SOLUTION, MAIS PROCHE

je devais limitons ma demande afin d'éviter une réponse 429 déclenchée par trop de demandes.

Solution possible, en supposant que vous avez déjà implémenté votre module réseau en utilisant d'autres technologies que NSOperation.

Utilisez GCD pour résoudre ce problème. Le code suivant introduit un délai d'une seconde pour chaque appel réseau. Portez une attention particulière au paramètre pour popTime

- (void)main { 

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

// Votre code réseau va ici

- (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 
    }); 
} 
+0

Ceci est complètement incorrect, il n'évalue pas la limite du tout ... Tout ce qu'il fait est de faire les appels réseau 1 seconde plus tard, mais autant d'entre eux par seconde. –

+0

@ einsteinx2 pouvez-vous s'il vous plaît lire à nouveau la question OP, il demande. "Mais comment limitez-vous une application à faire, disons, une seule requête par seconde? " – Edwin

+0

Oui, je comprends cela. Quoi qu'il en soit, je viens de relire votre réponse et de voir que vous faites en fait une simple forme de limitation de débit en divisant le délai en 10 secondes. J'avais raté la division et je pensais que vous étiez en train de faire tout l'appel après un délai d'une seconde. Je suppose que j'étais juste fatigué, désolé pour ça. J'ai supprimé mon vote négatif. –

Questions connexes