Risposte:
Penso che tu stia cercando dispatch_after()
. Richiede che il tuo blocco non accetti alcun parametro, ma puoi semplicemente consentire al blocco di catturare quelle variabili dal tuo ambito locale.
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);
});
Altro: https://developer.apple.com/documentation/dispatch/1452876-dispatch_after
dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)
frammento è cattivo. Non c'è un modo più pulito per questo?
dispatch_get_current_queue()
restituisce sempre la coda da cui viene eseguito il codice. Quindi quando questo codice viene eseguito dal thread principale, il blocco verrà eseguito anche sul thread principale.
dispatch_get_current_queue()
è deprecato ora
Puoi usare dispatch_after
per chiamare un blocco in seguito. In Xcode, inizia a digitare dispatch_after
e premi Enter
per completare automaticamente quanto segue:
Ecco un esempio con due float come "argomenti". Non devi fare affidamento su alcun tipo di macro e l'intento del codice è abbastanza chiaro:
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
avrebbe funzionato come NSEC_PER_MSEC * 500
. Mentre hai ragione a notare che si dispatch_time
aspetta un numero intero a 64 bit, il valore che si aspetta è in nanosecondi. NSEC_PER_SEC
è definito come 1000000000ull
e moltiplicando quello con una costante a virgola mobile si 0.5
comporterebbe implicitamente un'aritmetica a virgola mobile, cedendo 500000000.0
, prima che venga esplicitamente ricondotto a un numero intero a 64 bit. Quindi è perfettamente accettabile usare una frazione di NSEC_PER_SEC
.
Che ne dici di usare la libreria di snippet di codice integrata Xcode?
Aggiornamento per Swift:
Molti voti positivi mi hanno ispirato ad aggiornare questa risposta.
La libreria di snippet di codice Xcode integrata ha dispatch_after
solo la objective-c
lingua. Le persone possono anche creare il proprio snippet di codice personalizzato per Swift
.
Scrivi questo 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#>
})
Trascina questo codice e rilascialo nell'area della libreria dello snippet di codice.
In fondo all'elenco dei frammenti di codice, ci sarà una nuova entità denominata My Code Snippet
. Modifica questo per un titolo. Per suggerimenti durante la digitazione del codice X, compilare il Completion Shortcut
.
Per ulteriori informazioni, consultare CreatingaCustomCodeSnippet .
Trascina questo codice e rilascialo nell'area della libreria dello snippet di codice.
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
<#code to be executed after a specified delay#>
}
Espandendo la risposta di Jaime Cham, ho creato una categoria NSObject + Blocks come di seguito. Ho ritenuto che questi metodi corrispondessero meglio ai performSelector:
metodi NSObject esistenti
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
e usare così:
[anyObject performBlock:^{
[anotherObject doYourThings:stuff];
} afterDelay:0.15];
delay
dovrebbe essere di NSTimeInterval
(che è una double
). #import <UIKit/UIKit.h>
non è necessario. E non vedo perché - (void)performBlock:(void (^)())block;
potrebbe essere utile, quindi può essere rimosso dall'intestazione.
Forse più semplice che passare attraverso GCD, in una classe da qualche parte (ad esempio "Util") o una categoria su oggetto:
+ (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];
}
Quindi per usare:
[Util runAfterDelay:2 block:^{
NSLog(@"two seconds later!");
}];
Per Swift ho creato una funzione globale, niente di speciale, usando il dispatch_after
metodo. Mi piace di più in quanto è leggibile e facile da usare:
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)
}
Che puoi usare come segue:
performBlock({ () -> Void in
// Perform actions
}, afterDelay: 0.3)
after
. Quindi puoi scrivere:after(2.0){ print("do somthing") }
Ecco i miei 2 centesimi = 5 metodi;)
Mi piace incapsulare questi dettagli e AppCode mi dice come finire le mie frasi.
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 prende sempre un oggetto, quindi per passare argomenti come int / double / float ecc ..... Puoi usare qualcosa del genere.
// NSNumber è un oggetto ..
[self performSelector:@selector(setUserAlphaNumber:)
withObject: [NSNumber numberWithFloat: 1.0f]
afterDelay:1.5];
-(void) setUserAlphaNumber: (NSNumber*) number{
[txtUsername setAlpha: [number floatValue] ];
}
Allo stesso modo puoi usare [NSNumber numberWithInt:] ecc .... e nel metodo di ricezione puoi convertire il numero nel tuo formato come [numero int] o [numero doppio].
La funzione dispatch_after invia un oggetto a blocchi in una coda di invio dopo un determinato periodo di tempo. Utilizzare il codice seguente per eseguire alcuni tak relativi all'interfaccia utente dopo 2,0 secondi.
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 rapido 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,
invece dimainQueue)
Ecco un utile aiuto per evitare di fare ripetutamente la fastidiosa chiamata GCD :
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)
}
}
}
Ora puoi semplicemente ritardare il tuo codice sul thread principale in questo modo:
delay(bySeconds: 1.5) {
// delayed code
}
Se vuoi ritardare il tuo codice su thread diversi :
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Se preferisci un Framework che ha anche alcune funzioni più utili, dai un'occhiata a HandySwift . Puoi aggiungerlo al tuo progetto tramite Cartagine e usarlo esattamente come negli esempi sopra:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
In swift 3, possiamo semplicemente usare la funzione DispatchQueue.main.asyncAfter per attivare qualsiasi funzione o azione dopo il ritardo di 'n' secondi. Qui nel codice abbiamo impostato il ritardo dopo 1 secondo. Chiamate qualsiasi funzione all'interno del corpo di questa funzione che si attiverà dopo il ritardo di 1 secondo.
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {
// Trigger the function/action after the delay of 1Sec
}
Puoi racchiudere l'argomento nella tua classe o avvolgere la chiamata del metodo in un metodo che non deve essere passato nel tipo primitivo. Quindi chiama quel metodo dopo il tuo ritardo e, all'interno di quel metodo, esegui il selettore che desideri eseguire.
Ecco come è possibile attivare un blocco dopo un ritardo in Swift:
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)
}
È incluso come funzione standard nel mio repository .
Swift 3 e Xcode 8.3.2
Questo codice ti aiuterà, aggiungo anche una spiegazione
// 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 :)
}
}
}
Credo che l'autore non stia chiedendo come aspettare un tempo frazionario (ritardo), ma invece come passare uno scalare come argomento del selettore (conOggetto :) e il modo più veloce nel moderno obiettivo C è:
[obj performSelector:... withObject:@(0.123123123) afterDelay:10]
il tuo selettore deve cambiare il suo parametro in NSNumber e recuperare il valore usando un selettore come floatValue o doubleValue