wake-up-neo.com

Wie triggert man einen Block nach einer Verzögerung wie -performSelector: withObject: afterDelay :?

Gibt es eine Möglichkeit, einen Block mit einem primitiven Parameter nach einer Verzögerung aufzurufen, beispielsweise mit performSelector:withObject:afterDelay:, aber mit einem Argument wie int/double/float?

722
Egil

Ich denke, Sie suchen nach dispatch_after(). Es ist erforderlich, dass Ihr Block keine Parameter akzeptiert. Sie können den Block jedoch stattdessen diese Variablen aus Ihrem lokalen Bereich erfassen lassen.

int parameter1 = 12;
float parameter2 = 144.1;

// Delay execution of my block for 10 seconds.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
    NSLog(@"parameter1: %d parameter2: %f", parameter1, parameter2);
});

Mehr: https://developer.Apple.com/documentation/dispatch/1452876-dispatch_after

1158
Ryan

Sie können dispatch_after verwenden, um einen Block später aufzurufen. Beginnen Sie in Xcode mit der Eingabe von dispatch_after und drücken Sie Enter, um Folgendes automatisch zu vervollständigen:

enter image description here

Hier ist ein Beispiel mit zwei Gleitkommazahlen als "Argumente". Sie müssen sich nicht auf irgendeine Art von Makro verlassen, und die Absicht des Codes ist ganz klar:

Swift 3, Swift 4

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
    print("Sum of times: \(time1 + time2)")
}

Swift 2

let time1 = 8.23
let time2 = 3.42

// Delay 2 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
        println("Sum of times: \(time1 + time2)")
}

Ziel c

CGFloat time1 = 3.49;
CGFloat time2 = 8.13;

// Delay 2 seconds
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    CGFloat newTime = time1 + time2;
    NSLog(@"New time: %f", newTime);
});
487
Steven Hepting

Wie wäre es mit der in Xcode integrierten Code-Snippet-Bibliothek?

enter image description here

Update für Swift:

Viele positive Stimmen haben mich dazu inspiriert, diese Antwort zu aktualisieren.

Die eingebaute Xcode-Codefragmentbibliothek enthält _dispatch_after_ nur für die Sprache _objective-c_. Benutzer können auch ihr eigenes benutzerdefiniertes Code-Snippet für Swift erstellen.

Schreiben Sie dies in Xcode.

_dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(<#delayInSeconds#> * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), {
        <#code to be executed after a specified delay#>
    })
_

Ziehen Sie diesen Code und legen Sie ihn im Bereich der Codefragmentbibliothek ab. enter image description here

Am Ende der Code-Snippet-Liste befindet sich eine neue Entität mit dem Namen _My Code Snippet_. Bearbeiten Sie dies für einen Titel. Für Vorschläge, während Sie den Xcode eingeben, geben Sie den Completion Shortcut ein.

Weitere Informationen finden Sie unter CreatingaCustomCodeSnippet .

Aktualisieren Sie Swift 3

Ziehen Sie diesen Code und legen Sie ihn im Bereich der Codefragmentbibliothek ab.

_DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
    <#code to be executed after a specified delay#>
}
_
192

Als Erweiterung der Antwort von Jaime Cham habe ich eine NSObject + Blocks-Kategorie wie folgt erstellt. Ich war der Meinung, dass diese Methoden besser zu den vorhandenen performSelector: NSObject-Methoden passen

NSObject + Blocks.h

#import <Foundation/Foundation.h>

@interface NSObject (Blocks)

- (void)performBlock:(void (^)())block afterDelay:(NSTimeInterval)delay;

@end

NSObject + Blocks.m

#import "NSObject+Blocks.h"

@implementation NSObject (Blocks)

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

- (void)performBlock:(void (^)())block afterDelay:(NSTimeInterval)delay
{
    void (^block_)() = [block copy]; // autorelease this if you're not using ARC
    [self performSelector:@selector(performBlock:) withObject:block_ afterDelay:delay];
}

@end

und wie folgt verwenden:

[anyObject performBlock:^{
    [anotherObject doYourThings:stuff];
} afterDelay:0.15];
57
Oliver Pearmain

Möglicherweise einfacher als eine GCD zu durchlaufen, in einer Klasse irgendwo (z. B. "Util") oder in einer Kategorie für ein Objekt:

+ (void)runBlock:(void (^)())block
{
    block();
}
+ (void)runAfterDelay:(CGFloat)delay block:(void (^)())block 
{
    void (^block_)() = [[block copy] autorelease];
    [self performSelector:@selector(runBlock:) withObject:block_ afterDelay:delay];
}

Also zu benutzen:

[Util runAfterDelay:2 block:^{
    NSLog(@"two seconds later!");
}];
21
Jaime Cham

Für Swift habe ich mit der dispatch_after -Methode eine globale Funktion erstellt, nichts Besonderes. Mir gefällt dies besser, da es lesbar und einfach zu bedienen ist:

func performBlock(block:() -> Void, afterDelay delay:NSTimeInterval){
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), block)
}

Welche können Sie wie folgt verwenden:

performBlock({ () -> Void in
    // Perform actions
}, afterDelay: 0.3)
21
Antoine

Hier sind meine 2 Cent = 5 Methoden;)

Ich mag es, diese Details zusammenzufassen und AppCode sagt mir, wie ich meine Sätze beenden soll.

void dispatch_after_delay(float delayInSeconds, dispatch_queue_t queue, dispatch_block_t block) {
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
    dispatch_after(popTime, queue, block);
}

void dispatch_after_delay_on_main_queue(float delayInSeconds, dispatch_block_t block) {
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_after_delay(delayInSeconds, queue, block);
}

void dispatch_async_on_high_priority_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), block);
}

void dispatch_async_on_background_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), block);
}

void dispatch_async_on_main_queue(dispatch_block_t block) {
    dispatch_async(dispatch_get_main_queue(), block);
}
16
Dan Rosenstark

Die Funktion dispatch_after sendet ein Blockobjekt nach einem bestimmten Zeitraum an eine Dispatch-Warteschlange. Verwenden Sie den folgenden Code, um einige UI-bezogene Aufgaben nach 2,0 Sekunden auszuführen.

            let delay = 2.0
            let delayInNanoSeconds = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
            let mainQueue = dispatch_get_main_queue()

            dispatch_after(delayInNanoSeconds, mainQueue, {

                print("Some UI related task after delay")
            })

In Swift 3.0:

            let dispatchTime: DispatchTime = DispatchTime.now() + Double(Int64(2.0 * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC)
            DispatchQueue.main.asyncAfter(deadline: dispatchTime, execute: {

          })
8

PerformSelector: WithObject nimmt immer ein Objekt an. Um also Argumente wie int/double/float usw. zu übergeben, können Sie so etwas verwenden.

// NSNumber ist ein Objekt ..

[self performSelector:@selector(setUserAlphaNumber:)
     withObject: [NSNumber numberWithFloat: 1.0f]       
     afterDelay:1.5];



-(void) setUserAlphaNumber: (NSNumber*) number{

     [txtUsername setAlpha: [number floatValue] ];

}

Ebenso können Sie [NSNumber numberWithInt:] etc .... verwenden und in der Empfangsmethode die Zahl in Ihr Format als [number int] oder [number double] konvertieren.

8
Augustine

Hier ist die Swift 3 Möglichkeit, die Arbeit nach einer Verzögerung in die Warteschlange zu stellen.

DispatchQueue.main.asyncAfter(
  DispatchTime.now() + DispatchTimeInterval.seconds(2)) {
    // do work
}
5
cpimhoff

Hier ist ein praktischer Helfer, um zu verhindern, dass der lästige GCD-Aufruf immer wieder ausgeführt wird:

public func delay(bySeconds seconds: Double, dispatchLevel: DispatchLevel = .main, closure: @escaping () -> Void) {
    let dispatchTime = DispatchTime.now() + seconds
    dispatchLevel.dispatchQueue.asyncAfter(deadline: dispatchTime, execute: closure)
}

public enum DispatchLevel {
    case main, userInteractive, userInitiated, utility, background
    var dispatchQueue: DispatchQueue {
        switch self {
        case .main:                 return DispatchQueue.main
        case .userInteractive:      return DispatchQueue.global(qos: .userInteractive)
        case .userInitiated:        return DispatchQueue.global(qos: .userInitiated)
        case .utility:              return DispatchQueue.global(qos: .utility)
        case .background:           return DispatchQueue.global(qos: .background)
        }
    }
}

Jetzt kannst du einfach deinen Code im Haupt-Thread verzögern so:

delay(bySeconds: 1.5) { 
    // delayed code
}

Wenn Sie Ihren Code auf einen anderen Thread verschieben:

delay(bySeconds: 1.5, dispatchLevel: .background) { 
    // delayed code that will run on background thread
}

Wenn Sie ein Framework bevorzugen, das auch weitere nützliche Funktionen bietet, dann checkout HandySwift. Sie können es zu Ihrem Projekt hinzufügen über Carthage und dann genau wie in den obigen Beispielen verwenden:

import HandySwift    

delay(bySeconds: 1.5) { 
    // delayed code
}
5
Dschee

In Swift 3 können wir einfach die Funktion DispatchQueue.main.asyncAfter verwenden, um eine Funktion oder Aktion nach einer Verzögerung von 'n' Sekunden auszulösen. Hier im Code haben wir die Verzögerung nach 1 Sekunde eingestellt. Sie rufen jede Funktion im Hauptteil dieser Funktion auf, die nach einer Verzögerung von 1 Sekunde ausgelöst wird.

let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {

    // Trigger the function/action after the delay of 1Sec

}
4
Rouny

Es gibt ein Nettes im BlocksKit-Framework.

BlocksKit

(und die Klasse)

BBlocksKit.m

4
psy

Sie können das Argument entweder in Ihre eigene Klasse einschließen oder den Methodenaufruf in eine Methode einschließen, die nicht im primitiven Typ übergeben werden muss. Rufen Sie dann diese Methode nach Ihrer Verzögerung auf und führen Sie innerhalb dieser Methode den Selektor aus, den Sie ausführen möchten.

1
GendoIkari

So können Sie einen Block nach einer Verzögerung in Swift auslösen:

runThisAfterDelay(seconds: 2) { () -> () in
    print("Prints this 2 seconds later in main queue")
}

/// EZSwiftExtensions
func runThisAfterDelay(seconds seconds: Double, after: () -> ()) {
    let time = dispatch_time(DISPATCH_TIME_NOW, Int64(seconds * Double(NSEC_PER_SEC)))
    dispatch_after(time, dispatch_get_main_queue(), after)
}

Es ist als Standardfunktion in my repo enthalten.

1
Esqarrouth

Swift 3 & Xcode 8.3.2

Dieser Code wird Ihnen helfen, ich füge auch eine Erklärung hinzu

// Create custom class, this will make your life easier
class CustomDelay {

    static let cd = CustomDelay()

    // This is your custom delay function
    func runAfterDelay(_ delay:Double, closure:@escaping ()->()) {
        let when = DispatchTime.now() + delay
        DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
    }
}


// here how to use it (Example 1)
class YourViewController: UIViewController {

    // example delay time 2 second
    let delayTime = 2.0

    override func viewDidLoad() {
        super.viewDidLoad()

        CustomDelay.cd.runAfterDelay(delayTime) {
            // This func will run after 2 second
            // Update your UI here, u don't need to worry to bring this to the main thread because your CustomDelay already make this to main thread automatically :)
            self.runFunc()
        }
    }

    // example function 1
    func runFunc() {
        // do your method 1 here
    }
}

// here how to use it (Example 2)
class YourSecondViewController: UIViewController {

    // let say you want to user run function shoot after 3 second they tap a button

    // Create a button (This is programatically, you can create with storyboard too)
    let shootButton: UIButton = {
        let button = UIButton(type: .system)
        button.frame = CGRect(x: 15, y: 15, width: 40, height: 40) // Customize where do you want to put your button inside your ui
        button.setTitle("Shoot", for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // create an action selector when user tap shoot button
        shootButton.addTarget(self, action: #selector(shoot), for: .touchUpInside)   
    }

    // example shoot function
    func shoot() {
        // example delay time 3 second then shoot
        let delayTime = 3.0

        // delay a shoot after 3 second
        CustomDelay.cd.runAfterDelay(delayTime) {
            // your shoot method here
            // Update your UI here, u don't need to worry to bring this to the main thread because your CustomDelay already make this to main thread automatically :)
        }
    }   
}
1

Ich glaube, der Autor fragt nicht, wie man auf einen Bruchteil der Zeit wartet (Verzögerung), sondern wie man einen Skalar als Argument des Selektors übergibt (withObject :) und der schnellste Weg in modernem Objective C ist:

[obj performSelector:...  withObject:@(0.123123123) afterDelay:10]

ihr Selektor muss seinen Parameter in NSNumber ändern und den Wert mit einem Selektor wie floatValue oder doubleValue abrufen

0
André

Xcode 10.2 und Swift 5 und höher

DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
   // code to execute                 
})
0
midhun p