Gibt es eine Möglichkeit, einen Block mit einem primitiven Parameter nach einer Verzögerung aufzurufen, z. B. performSelector:withObject:afterDelay:
mit einem Argument wie int
/ double
/ float
?
Gibt es eine Möglichkeit, einen Block mit einem primitiven Parameter nach einer Verzögerung aufzurufen, z. B. performSelector:withObject:afterDelay:
mit einem Argument wie int
/ double
/ float
?
Antworten:
Ich denke du suchst dispatch_after()
. Es erfordert, dass Ihr Block keine Parameter akzeptiert, aber Sie können den Block stattdessen einfach 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);
});
Weitere Informationen : https://developer.apple.com/documentation/dispatch/1452876-dispatch_after
dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)
Ausschnitt ist böse. Gibt es dafür keinen saubereren Weg?
dispatch_get_current_queue()
immer die Warteschlange zurück, aus der der Code ausgeführt wird. Wenn dieser Code vom Hauptthread ausgeführt wird, wird der Block auch im Hauptthread ausgeführt.
dispatch_get_current_queue()
ist jetzt veraltet
Sie können dispatch_after
später einen Block aufrufen. Beginnen Sie in Xcode mit der Eingabe dispatch_after
und Enter
drücken Sie, um die folgenden Schritte automatisch zu vervollständigen:
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:
let time1 = 8.23
let time2 = 3.42
// Delay 2 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
print("Sum of times: \(time1 + time2)")
}
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)")
}
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);
});
NSEC_PER_SEC * 0.5
würde das genauso funktionieren wie NSEC_PER_MSEC * 500
. Während Sie zu Recht feststellen, dispatch_time
dass eine 64-Bit-Ganzzahl erwartet wird, wird der erwartete Wert in Nanosekunden angegeben. NSEC_PER_SEC
ist definiert als 1000000000ull
und das Multiplizieren mit einer Gleitkommakonstante 0.5
würde implizit eine Gleitkomma-Arithmetik ausführen, die sich ergibt 500000000.0
, bevor sie explizit in eine 64-Bit-Ganzzahl zurückgesetzt wird. Es ist also durchaus akzeptabel, einen Bruchteil von zu verwenden NSEC_PER_SEC
.
Wie wäre es mit der in Xcode integrierten Code-Snippet-Bibliothek?
Update für Swift:
Viele Stimmen haben mich dazu inspiriert, diese Antwort zu aktualisieren.
Die integrierte Xcode-Code-Snippet-Bibliothek ist dispatch_after
nur für die objective-c
Sprache verfügbar . Benutzer können auch ihr eigenes benutzerdefiniertes Code-Snippet für erstellen Swift
.
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 Code-Snippet-Bibliothek ab.
Am Ende der Code-Snippet-Liste wird eine neue Entität mit dem Namen angezeigt My Code Snippet
. Bearbeiten Sie dies für einen Titel. Für Vorschläge, während Sie den Xcode eingeben, füllen Sie das Feld aus Completion Shortcut
.
Weitere Informationen finden Sie unter Erstellen eines CustomCodeSnippets .
Ziehen Sie diesen Code und legen Sie ihn im Bereich der Code-Snippet-Bibliothek ab.
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
<#code to be executed after a specified delay#>
}
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 benutze so:
[anyObject performBlock:^{
[anotherObject doYourThings:stuff];
} afterDelay:0.15];
delay
sollte von sein NSTimeInterval
(was a ist double
). #import <UIKit/UIKit.h>
wird nicht gebraucht. Und ich verstehe nicht, warum - (void)performBlock:(void (^)())block;
nützlich sein könnte, kann also aus dem Header entfernt werden.
Vielleicht einfacher als GCD zu durchlaufen, in einer Klasse irgendwo (zB "Util") oder in einer Kategorie nach 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!");
}];
Für Swift habe ich mit dieser dispatch_after
Methode eine globale Funktion erstellt, die nichts Besonderes ist . Ich mag das mehr, 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)
after
. Dann können Sie schreiben:after(2.0){ print("do somthing") }
Hier sind meine 2 Cent = 5 Methoden;)
Ich mag es, diese Details zu kapseln 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);
}
PerformSelector: WithObject nimmt immer ein Objekt, um Argumente wie int / double / float usw. zu übergeben. Sie können 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] ];
}
Auf die gleiche Weise können Sie [NSNumber numberWithInt:] etc .... verwenden und in der Empfangsmethode die Nummer in Ihr Format als [number int] oder [number double] konvertieren.
Die Funktion dispatch_after sendet nach einer bestimmten Zeit ein Blockobjekt an eine Versandwarteschlange. Verwenden Sie den folgenden Code, um nach 2,0 Sekunden einige UI-bezogene Takes durchzufü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 schnellem 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: {
})
mainQueue,
anstelle vonmainQueue)
Hier ist ein praktischer Helfer , um zu verhindern, dass der nervige GCD immer wieder aufgerufen 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 verzögern Sie einfach Ihren Code im Haupt-Thread wie folgt:
delay(bySeconds: 1.5) {
// delayed code
}
Wenn Sie Ihren Code auf einen anderen Thread verschieben möchten :
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Wenn Sie ein Framework bevorzugen , das auch einige weitere praktische Funktionen bietet, lesen Sie HandySwift . Sie können es über Karthago zu Ihrem Projekt hinzufügen und dann genau wie in den obigen Beispielen verwenden:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
In Swift 3 können wir einfach die Funktion DispatchQueue.main.asyncAfter verwenden, um eine Funktion oder Aktion nach der 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
}
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 diese Methode nach Ihrer Verzögerung auf und führen Sie innerhalb dieser Methode den Selektor aus, den Sie ausführen möchten.
So können Sie nach einer Verzögerung in Swift einen Block 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 meinem Repo enthalten .
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 :)
}
}
}
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 im modernen Ziel 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