¿Cómo se desencadena un locking después de un retraso, como -performSelector: withObject: afterDelay :?

¿Hay una manera de llamar a un bloque con un parámetro primitivo después de un retraso, como usar performSelector:withObject:afterDelay: pero con un argumento como int / double / float ?

Creo que estás buscando dispatch_after() . Requiere que su bloque no acepte ningún parámetro, pero puede dejar que el bloque capture esas variables desde su ámbito local.

 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); }); 

Más: https://developer.apple.com/documentation/dispatch/1452876-dispatch_after

Puede usar dispatch_after para llamar a un bloque más tarde. En Xcode, comience a escribir dispatch_after y presione Enter para autocompletar a lo siguiente:

enter image description here

Aquí hay un ejemplo con dos carrozas como “argumentos”. No tiene que depender de ningún tipo de macro, y la intención del código es bastante clara:

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)") } 

C objective

 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); }); 

¿Qué le parece usar la biblioteca de fragmentos de código incorporados de Xcode?

enter image description here

Actualización para Swift:

Muchos votos me inspiraron a actualizar esta respuesta.

La biblioteca de fragmentos de código Xcode incorporada tiene dispatch_after solo para el lenguaje objective-c . Las personas también pueden crear su propio fragmento de código personalizado para Swift .

Escribe esto en 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#> }) 

Arrastre este código y suéltelo en el área de la biblioteca de fragmentos de código. enter image description here

En la parte inferior de la lista de fragmentos de código, habrá una nueva entidad llamada My Code Snippet . Edita esto por un título. Para sugerencias a medida que escribe en Xcode complete el Completion Shortcut .

Para obtener más información, consulte CreatingaCustomCodeSnippet .

Actualiza Swift 3

Arrastre este código y suéltelo en el área de la biblioteca de fragmentos de código.

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

Ampliando la respuesta de Jaime Cham, creé una categoría NSObject + Blocks como se muestra a continuación. Sentí que estos métodos coincidían mejor con el performSelector: existente performSelector: métodos de NSObject

NSObject + Blocks.h

 #import  @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 

y usar así:

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

Tal vez más simple que ir a través de GCD, en una clase en algún lugar (por ejemplo, “Util”) o una Categoría en Object:

 + (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]; } 

Entonces para usar:

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

Para Swift, he creado una función global, nada especial, utilizando el método dispatch_after . Me gusta más porque es legible y fácil de usar:

 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) } 

Que puedes usar de la siguiente manera:

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

Aquí están mis 2 centavos = 5 métodos;)

Me gusta encapsular estos detalles y tener AppCode dime cómo terminar mis oraciones.

 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 siempre toma un objeto, por lo tanto, para pasar argumentos como int / double / float, etc. ….. Puede usar algo como esto.

// NSNumber es un objeto …

 [self performSelector:@selector(setUserAlphaNumber:) withObject: [NSNumber numberWithFloat: 1.0f] afterDelay:1.5]; -(void) setUserAlphaNumber: (NSNumber*) number{ [txtUsername setAlpha: [number floatValue] ]; } 

De la misma manera puede usar [NSNumber numberWithInt:] etc …. y en el método de recepción puede convertir el número a su formato como [number int] o [number double].

La función dispatch_after distribuye un objeto de bloque a una cola de despacho después de un período de tiempo determinado. Use el código siguiente para realizar algunas tomas relacionadas con la interfaz de usuario después de 2.0 segundos.

  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") }) 

En 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: { }) 

Aquí está la forma de Swift 3 para cola de trabajo después de un retraso.

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

Aquí hay un práctico ayudante para evitar hacer la molesta llamada de GCD una y otra vez:

 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) } } } 

Ahora simplemente demora su código en el hilo principal como este:

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

Si quieres retrasar tu código a un hilo diferente :

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

Si prefiere un Framework que también tenga algunas características más útiles, realice la compra de HandySwift . Puede agregarlo a su proyecto a través de Cartago y luego usarlo exactamente como en los ejemplos anteriores:

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

Hay uno bueno en el marco de BlocksKit.

BlocksKit

(y la clase)

BBlocksKit.m

En swift 3, simplemente podemos usar la función DispatchQueue.main.asyncAfter para desencadenar cualquier función o acción después del retraso de ‘n’ segundos. Aquí en el código hemos establecido la demora después de 1 segundo. Llama a cualquier función dentro del cuerpo de esta función que se disparará después de la demora de 1 segundo.

 let when = DispatchTime.now() + 1 DispatchQueue.main.asyncAfter(deadline: when) { // Trigger the function/action after the delay of 1Sec } 

Puede envolver el argumento en su propia clase o ajustar la llamada al método en un método que no necesita pasar en el tipo primitivo. Luego llame a ese método después de su retraso, y dentro de ese método realice el selector que desea realizar.

Aquí se explica cómo puede activar un locking después de un retraso en 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) } 

Está incluido como una función estándar en mi repository .

Swift 3 y Xcode 8.3.2

Este código te ayudará, también añado una explicación

 // 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 progtwigtically, 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 :) } } } 

Creo que el autor no está preguntando cómo esperar un tiempo fraccional (retraso), sino cómo pasar un escalar como argumento del selector (con Objeto 🙂 y la manera más rápida en el objective moderno C es:

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

su selector tiene que cambiar su parámetro a NSNumber, y recuperar el valor usando un selector como floatValue o doubleValue

 [NSTimer scheduledTimerWithTimeInterval:2.5 target:self selector:@selector(toDoSomething) userInfo:nil repeats:NO];