5

Ich habe eine for-Schleife, die ich eine Verzögerung zwischen Wiederholungen hinzufügen möchten. Ich habe waitUntilDone auf YES geändert und die gleichen Ergebnisse erhalten. Mein Array hat nur zwei Zahlen in sie, und beide werden nach den fünf Sekunden gerufen statt:Objective C für Schleifenverzögerung

0s - nichts 5s - Block genannt 10s- Block-

genannt
for(NSNumber* transaction in gainsArray) { 

    double delayInSeconds = 5.0; 
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
    dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { 

     NSLog(@"Block"); 

     [self performSelectorOnMainThread:@selector(private_addTransactionToBankroll:) 
     withObject:transaction waitUntilDone:NO]; 

    }); 
} 

2015-06-16 20:11:06.485 TestApp[97027:6251126] Block 
2015-06-16 20:11:06.485 TestApp[97027:6251127] Block 

Ich verwende Cocos2d wenn es ankommt

+0

eine 'NSTimer' Verwendung ist auch eine gute Lösung. – zaph

+0

@zaph NSTimer spielt nicht gut mit cocos2d oder SpriteKit. Bevorzugen Sie die eingebauten 'Schedule'-Methoden, die von CCNode und all seinen abgeleiteten Klassen bereitgestellt werden. – YvesLeBorg

Antwort

4

die for-Schleife wird eines nach dem anderen versenden, so dass sie im wesentlichen zur gleichen Zeit verzögern.
Set statt eine andere zunehmende Verzögerung für jeden:

double delayInSeconds = 0.0; 
for(NSNumber* transaction in gainsArray) 
{ 
    delayInSeconds += 5.0; 
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); 
    dispatch_after(popTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) 
        { 
         NSLog(@"Block"); 
         [self performSelectorOnMainThread:@selector(private_addTransactionToBankroll:) 
               withObject:transaction 
              waitUntilDone:NO]; 

       }); 
} 
+1

Es funktioniert perfekt! Vielen Dank! – Asdrubal

+0

Es funktioniert wie eine Magie !! Upvoted .. – NSPratik

1

@zaph eine ziemlich gute Lösung. Ich dachte, ich würde es aus einem anderen Blickwinkel versuchen. Da Objective-C Ziel -C ist, warum nicht eine Art von Objekt definieren, dies zu tun timed Looping? Tipp: Das existiert. Wir können NSTimer und seine userInfo-Eigenschaft verwenden, um dies auszuarbeiten. Ich denke, die Lösung ist eine Art eleganter, wenn nicht ein hässlicher Hack.

// Somewhere in code.... to start the 'loop' 
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5.0 
                target:self 
                action:@selector(processNextTransaction:) 
               userInfo:@{ 
                  @"gains": [gainsArray mutableCopy] 
                  } 
               repeats:NO]; 

// What handles each 'iteration' of your 'loop' 
- (void)processNextTransaction:(NSTimer *)loopTimer { 
    NSMutableArray *gains = [loopTimer.userInfo objectForKey:@"gains"]; 
    if(gains && gains.count > 0) { 
     id transaction = [gains firstObject]; 
     [gains removeObjectAtIndex:0]; // NSMutableArray should really return the object we're removing, but it doesn't... 
     [self private_addTransactionToBankroll:transaction]; 
     NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:5.0 
                  target:self 
                  action:@selector(processNextTransaction:) 
                 userInfo:@{ 
                    @"gains": gains 
                    } 
                 repeats:NO]; 
    } 
} 

Ich würde überprüfen, dass der NSTimer beibehalten wird, indem der Run-Loop hinzugefügt wird. Wenn dies nicht der Fall ist, sollten Sie einen Verweis darauf als Eigenschaft für jede Klasse speichern, die all dies verwaltet.

Es ist auch erwähnenswert, dass, weil NSTimers auf dem Hauptlaufschleife standardmäßig installiert werden, müssen Sie sich keine Gedanken über all die GCD Sachen kümmern. Dann wieder, wenn diese Arbeit ziemlich schwierige Arbeit ist, können Sie -processNextTransaction: wollen ihre Arbeit auf eine andere GCD Warteschlange abzuladen und dann in die Hauptwarteschlange wieder auf die NSTimer Instanz zu initialisieren.

Verwenden Sie unbedingt die -scheduledTimer... Methode; timer... Klassenmethoden auf NSTimer installieren Sie es nicht auf einer beliebigen Schleife, und die Objekte sitzen nur im Raum nichts zu tun. Nicht repeats:YES tun, das wäre tragisch, wie Sie auf die Laufschleife nolens volens, angebracht Timer haben würde, um sie mit keine Hinweise Hinweis zu wissen, wie oder wo sie zu stoppen. Das ist generell eine schlechte Sache.

Um EXC_BAD_ACCESS Ausnahmen zu vermeiden, das Objekt niemals freigeben, dessen Methode NSTimer aufrufen wird, wenn dieser Timer noch nicht ausgelöst wurde. Möglicherweise möchten Sie die ausstehende NSTimer in einer Eigenschaft in Ihrer Klasse speichern, so dass Sie mit dieser Art von Sache umgehen können. Wenn es ein ViewController ist, der all dies verwaltet (was es normalerweise ist), würde ich den folgenden Code verwenden, um den Timer auf -viewWillDisappear zu bereinigen. (Dies setzt voraus, dass Sie einen neuen Timer zu einem gewissen @property sind Einstellung, self.timer)

- (void)viewWillDisappear:(BOOL)animated { 
    [super viewWillDisappear:animated]; 
    if(self.timer) { 
     [self.timer invalidate]; // -invalidate removes it from the run loop. 
     self.timer = nil; // Stop pointing at it so ARC destroys it. 
    } 
} 
+0

Vielen Dank für die Einnahme des Timers, um eine andere Lösung zu bieten und es gut zu erklären. Ich schätze es. – Asdrubal

+1

Natürlich! Die eingehende Beantwortung von Fragen ist für mich eine gute Möglichkeit, dafür zu sorgen, dass ich scharf bleibe, also halte ich das für eine Win-Win-Situation. – josefdlange