Hinzufügen einer Verzögerung zwischen der Ausführung von zwei folgenden Zeilen

78

Ich muss eine Verzögerung zwischen der Ausführung von zwei Zeilen in einer (gleichen) Funktion hinzufügen. Gibt es dafür günstige Möglichkeiten?

Hinweis: Ich benötige dazu keine zwei verschiedenen Funktionen, und die Verzögerung darf die Ausführung anderer Funktionen nicht beeinflussen.

z.B:

line 1: [executing first operation];

line 2: Delay                        /* I need to introduce delay here */

line 3: [executing second operation];

Jede Hilfe ist spürbar. Danke im Voraus...

Krishna Raj Salim
quelle

Antworten:

208

Sie können dazu gcd verwenden, ohne eine andere Methode erstellen zu müssen

double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
  NSLog(@"Do some work");
});

Sie sollten sich immer noch fragen, ob ich wirklich eine Verzögerung hinzufügen muss, da dies häufig den Code kompliziert und zu Rennbedingungen führen kann

Paul.s
quelle
2
Es ist richtig, Leute darüber zu warnen, aber wenn Sie Controller codieren, die auf ein externes Gerät mit einer festgelegten Verarbeitungszeit warten müssen, ist dies NICHT unsinnig.
user2161301
3
Wie wäre es mit einer Verzögerung einer HTTP-Anfrage beim Schreiben in eine Suchleiste? Ergebnisse während der Eingabe filtern? Eine Sekunde zu warten, bevor eine gefilterte Liste angefordert wird, anstatt den Server nach jedem Schlüssel abzufragen, auf den der Benutzer tippt, klingt für mich nicht nach Unsinn.
Alejandro Iván
26

Sie können die NSThreadMethode verwenden:

[NSThread sleepForTimeInterval: delay];

Wenn Sie dies jedoch im Hauptthread tun, blockieren Sie die App. Tun Sie dies also nur in einem Hintergrundthread.


oder in Swift

NSThread.sleepForTimeInterval(delay)

in Swift 3

Thread.sleep(forTimeInterval: delay)
Ashley Mills
quelle
2
Ich glaube, dies hätte die akzeptierte Antwort gemäß den Anforderungen der Frage sein müssen.
ACLima
1
@ACLima: Entschuldigung, 'sleepForTimeInterval' wird den Thread für ein bestimmtes Zeitintervall in den Ruhezustand versetzen, was sich auf die gleichzeitige Ausführung anderer Funktionen auswirken kann.
Krishna Raj Salim
seine zweite oder monute?
Shiva
20

Diese Zeile ruft nach 3 Sekunden den Selektor secondMethod auf:

[self performSelector:@selector(secondMethod) withObject:nil afterDelay:3.0 ];

Verwenden Sie es bei Ihrer zweiten Operation mit der gewünschten Verzögerung. Wenn Sie viel Code haben, platzieren Sie ihn in einer eigenen Methode und rufen Sie diese Methode mit auf performSelector:. Es wird die Benutzeroberfläche nicht wie blockierensleep

Bearbeiten: Wenn Sie keine zweite Methode möchten, können Sie eine Kategorie hinzufügen, um Blöcke mit performSelector verwenden zu können:

@implementation NSObject (PerformBlockAfterDelay)

- (void)performBlock:(void (^)(void))block 
          afterDelay:(NSTimeInterval)delay
{
    block = [block copy];
    [self performSelector:@selector(fireBlockAfterDelay:) 
               withObject:block 
               afterDelay:delay];
}

- (void)fireBlockAfterDelay:(void (^)(void))block
{
    block();
}

@end

Oder vielleicht sogar sauberer:

void RunBlockAfterDelay(NSTimeInterval delay, void (^block)(void))
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC*delay),
      dispatch_get_current_queue(), block);
}
Sunkas
quelle
6
Das OP erklärt ausdrücklich, dass sie keine zweite Methode wollen: S
Pauls
Er könnte eine Kategorie hinzufügen, um Blöcke verwenden zu können mit performSelector: stackoverflow.com/questions/4007023/…
Sunkas
@ Sunkas: Danke für die Antwort. Aber ich habe bereits erwähnt, dass ich keine zweite Funktion hinzufügen möchte.
Krishna Raj Salim
3
Wenn Sie diese Antwort verwenden, können Sie Code nach einer Verzögerung ohne eine zweite Methode ganz sauber ausführen. [self performBlock:^{ your_code } afterDelay:0.1];
Sunkas
4
Wenn das OP nicht bereit ist, eine andere Methode hinzuzufügen, sollten sie erklären, warum, da dies eine sehr gute Möglichkeit ist, die Tools so zu verwenden, wie sie funktionieren sollen. Sind Methoden schlecht? Zahlen sie durch die (nichtige)?
Tooluser
8

Ich habe ein paar rundenbasierte Spiele, bei denen die KI eine Pause einlegen muss, bevor sie an die Reihe kommt (und zwischen den Schritten in ihrem Zug). Ich bin sicher, dass es andere, nützlichere Situationen gibt, in denen eine Verzögerung die beste Lösung ist. In Swift:

        let delay = 2.0 * Double(NSEC_PER_SEC) 
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) 
        dispatch_after(time, dispatch_get_main_queue()) { self.playerTapped(aiPlayView) }

Ich bin gerade hierher zurückgekommen, um zu sehen, ob die Objective-C-Aufrufe unterschiedlich waren. (Ich muss dies auch zu diesem hinzufügen.)

David Reich
quelle
4

Wenn Sie auf iOS 4.0+ abzielen, können Sie Folgendes tun:

[executing first operation];
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
    [executing second operation];
});
Attila H.
quelle
1
Du meinst iOS 4? Grand Central Dispatch Funktionsreferenz
Pauls
3

Der derzeit bequemste Weg: Xcode bietet dazu ein Code-Snippet, bei dem Sie nur den Verzögerungswert und den Code eingeben müssen, den Sie nach der Verzögerung ausführen möchten.

  1. Klicken Sie auf die +Schaltfläche oben rechts in Xcode.
  2. suchen nach after
  3. Es wird nur 1 Suchergebnis zurückgegeben, das das gewünschte Snippet ist (siehe Screenshot). Doppelklicken Sie darauf und los geht's.

Screenshot, der zeigt, wie das Snippet aus Xcode heraus abgerufen wird

verheißungsvoll99
quelle
1

Wie @Sunkas schrieb, performSelector:withObject:afterDelay:ist das Pendant zu dem dispatch_afternur, dass es kürzer ist und Sie die normale objective-cSyntax haben. Wenn Sie Argumente an den Block übergeben müssen, den Sie verzögern möchten, können Sie sie einfach über den Parameter übergeben withObjectund Sie erhalten sie in dem selectorAufruf:

[self performSelector:@selector(testStringMethod:) 
           withObject:@"Test Test" 
           afterDelay:0.5];

- (void)testStringMethod:(NSString *)string{
    NSLog(@"string  >>> %@", string);
}

Wenn Sie sich dennoch selbst auswählen möchten, ob Sie es im Hauptthread oder im aktuellen Thread ausführen, gibt es bestimmte Methoden, mit denen Sie dies angeben können. In der Apples-Dokumentation heißt es:

Wenn Sie möchten, dass die Nachricht aus der Warteschlange entfernt wird, wenn sich die Ausführungsschleife in einem anderen Modus als dem Standardmodus befindet, verwenden Sie stattdessen die Methode performSelector: withObject: afterDelay: inModes :. Wenn Sie nicht sicher sind, ob der aktuelle Thread der Hauptthread ist, können Sie die Methode performSelectorOnMainThread: withObject: waitUntilDone: oder performSelectorOnMainThread: withObject: waitUntilDone: mode: verwenden, um sicherzustellen, dass Ihr Selektor auf dem Hauptthread ausgeführt wird. Verwenden Sie zum Abbrechen einer Nachricht in der Warteschlange die Methode cancelPreviousPerformRequestsWithTarget: oder cancelPreviousPerformRequestsWithTarget: selector: object:.

Alex Cio
quelle