2013-07-12 1 views
4

J'ai une fonction de boucle et appelée [NSThread sleepForTimeInterval: 2.0] ;. cela signifie après 2s, la fonction de boucle est appelée. Je veux quand passer une nouvelle vue, cette fonction de boucle est stop et quand elle revient, elle est appelée à nouveau.Puis-je arrêter ou annuler la fonction de boucle lors de l'utilisation de [NSThread sleepForTimeInterval: 2.0]; dans IOS

J'utiliser ce code pour fonction de boucle appel:

-(void) viewWillAppear:(BOOL)animated 
{ 
    [super viewWillAppear:animated]; 
    loop = YES; 
    delete=NO; 
    temp = [FileCompletedArray mutableCopy]; 
    NSOperationQueue *queue = [NSOperationQueue new]; 

    operations = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(updateArray) object:nil]; 
    [queue addOperation:operations]; 
    [operations release]; 

} 

et la fonction de boucle:

-(void)updateArray{ 

    while (loop) 
    { 
     NSLog(@"start loop"); 

     if(loop){ 
     [NSThread sleepForTimeInterval:2.0]; 
     NSLog(@"start send request"); 


     NSURL *url1 = [NSURL URLWithString:@"http://server.com"]; 


     NSMutableURLRequest *afRequest = [httpClient requestWithMethod:@"POST" path:nil parameters:params1] ; 

     operation= [[AFHTTPRequestOperation alloc] initWithRequest:afRequest]; 
     NSLog(@" request sent"); 
     [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { 

      NSLog(@"Server response1"); 

     } 
     failure:^(AFHTTPRequestOperation *operation, NSError *error) { 
      NSLog(@"error: %@", error); 
    } 
     ]; 
     [httpClient enqueueHTTPRequestOperation:operation]; 
    } 
     else 
      return; 
    } 

} 

Et viewdisappear()

-(void) viewWillDisappear:(BOOL)animated 
{ 
    [super viewWillDisappear:animated]; 
    loop = NO; 
    delete=NO; 
    [operations cancel] ; 
} 

Mon problème est quand adopter une nouvelle vue, updateArray appelle toujours. Ça ne s'arrête pas. Avez-vous une suggestion?

Répondre

2

Vous pouvez l'essayer en utilisant des observateurs de valeurs clés. Vous pouvez implémenter les modifications dans la méthode suivante qui sera automatiquement appelée en tant que modification de valeur particulière. Vous devez d'abord définir la notification pour l'ivar qui va être modifié.

[self addObserver:self forKeyPath:@"loop" options:NSKeyValueObservingOptionNew context:nil]; 

Ensuite, vous devez mettre en œuvre les changements selon l'exigence de la méthode suivante:

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
0

Il y a quelques problèmes qui sautent sur moi:

  1. Si votre l'intention est d'annuler le sleepForTimeInterval, je ne crois pas que vous pouvez le faire. Il existe d'autres mécanismes (tels que les minuteurs) qui sont bien mieux adaptés à ce problème. En outre, vous lancez une requête asynchrone toutes les deux secondes. Mais vous n'avez aucune garantie que votre demande précédente sera terminée dans ce laps de temps, cependant. Par conséquent, vous pouvez vous retrouver avec un retard de plusieurs demandes réseau qui continueront à s'exécuter après le rejet de la vue.

    J'aurais pensé que vous voudriez initier le "attendre deux secondes" à l'intérieur le bloc d'achèvement de votre demande asynchrone, pour s'assurer que vos demandes ne sont pas retardées derrière votre "toutes les deux secondes" logique. Clairement, cela ne fonctionnera pas avec votre boucle while actuelle sauf si vous faites une requête synchrone, donc vous pouvez refactoriser ce code, en remplaçant la boucle while par quelque chose qui effectue une seule requête, et dans le bloc d'achèvement, attend deux secondes avant d'initier la prochaine demande.

  2. Vous vérifiez l'état de loop, en attendant deux secondes, puis en émettant votre demande. Donc, si la vue disparaissait pendant les deux secondes de sommeil, il n'y a rien ici qui empêche d'émettre la requête après avoir fini de dormir.

    À tout le moins, si vous allez utiliser sleepForTimeInterval, vous voulez sans doute vérifier loop état après vous avez fini de dormir. Mais, à mon premier point, il est préférable d'utiliser un mécanisme annulable, comme une minuterie. Si vous dites que votre boucle ne se termine jamais, je vous suggère de vérifier que les méthodes d'apparence sont appelées comme vous le pensez.

Personnellement, je serais enclin à le faire avec une minuterie qui peut facilement être annulée/invalidée:

  1. Définir mes propriétés:

    @property (nonatomic, strong) NSTimer *timer; 
    @property (nonatomic, getter = isLooping) BOOL looping; 
    @property (nonatomic, weak) AFHTTPRequestOperation *operation; 
    
  2. avoir mon apparence les méthodes définissent la variable looping et démarrent/arrêtent les requêtes planifiées selon le cas:

    - (void)viewDidAppear:(BOOL)animated 
    { 
        self.looping = YES; 
        [self scheduleRequestIfLooping]; 
    } 
    
    - (void)viewWillDisappear:(BOOL)animated 
    { 
        self.looping = NO; 
        [self cancelScheduledRequest]; 
    } 
    
  3. Les méthodes qui font le démarrage et l'arrêt des demandes prévues utiliserait la NSTimer:

    - (void)scheduleRequestIfLooping 
    { 
        if ([self isLooping]) { 
         self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(initiateRequest:) userInfo:nil repeats:NO]; 
        } 
    } 
    
    - (void)cancelScheduledRequest 
    { 
        [self.timer invalidate]; 
        self.timer = nil; 
    
        // you might want to cancel any `AFHTTPRequestOperation` 
        // currently in progress, too 
    
        [self.operation cancel]; 
    } 
    

    Remarque, si la méthode cancel doit annuler à la fois la minuterie et toute demande actuelle en cours (le cas échéant) C'est à toi de voir.

  4. Enfin, mettez la planification de la demande suivante à l'intérieur le bloc d'achèvement de la demande en cours.

    - (void)initiateRequest:(NSTimer *)timer 
    { 
        // create AFHTTPRequestOperation 
    
        AFHTTPRequestOperation operation = ... 
    
        // now schedule next request _after_ this one, by initiating that request in the completion block 
    
        [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { 
         NSLog(@"Server response1"); 
         [self scheduleRequestIfLooping]; 
        } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 
         NSLog(@"error: %@", error); 
         // not sure if you want to schedule request if last one failed 
        }]; 
    
        self.operation = operation; // save this in the property 
    } 
    

Dans ce qui précède, j'utilise le thread principal pour la minuterie. Si vous voulez vraiment le faire sur une file d'attente en arrière-plan, vous le pouvez, mais (a) cela me semble inutile car les requêtes réseau se produisent déjà sur un thread d'arrière-plan; et (b) si vous faites cela sur un thread d'arrière-plan, vous voudrez peut-être vous assurer que vous faites la manipulation thread-safe nécessaire de vos variables d'état et autres. Cela m'a semblé une complication inutile.

Questions connexes