Respostas:
Eu acho que você está procurando dispatch_after()
. Ele exige que seu bloco não aceite parâmetros, mas você pode permitir que o bloco capture essas variáveis do seu escopo 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);
});
Mais: https://developer.apple.com/documentation/dispatch/1452876-dispatch_after
dispatch_time(DISPATCH_TIME_NOW, 10ull * NSEC_PER_SEC)
trecho é desagradável. Não existe uma maneira mais limpa para isso?
dispatch_get_current_queue()
sempre retorna a fila na qual o código está sendo executado. Portanto, quando esse código for executado no thread principal, o bloco também será executado no thread principal.
dispatch_get_current_queue()
está obsoleto agora
Você pode usar dispatch_after
para ligar para um bloco mais tarde. No Xcode, comece a digitar dispatch_after
e pressione Enter
para concluir automaticamente o seguinte:
Aqui está um exemplo com dois carros alegóricos como "argumentos". Você não precisa confiar em nenhum tipo de macro, e a intenção do código é bastante clara:
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
funcionaria da mesma forma que NSEC_PER_MSEC * 500
. Enquanto você está correto ao observar que dispatch_time
espera um número inteiro de 64 bits, o valor esperado é em nanossegundos. NSEC_PER_SEC
é definido como 1000000000ull
e multiplicar isso por uma constante de ponto flutuante 0.5
implicitamente executaria uma aritmética de ponto flutuante, produzindo 500000000.0
, antes que seja explicitamente convertida de volta para um número inteiro de 64 bits. Portanto, é perfeitamente aceitável usar uma fração de NSEC_PER_SEC
.
Que tal usar a biblioteca de trechos de código internos do Xcode?
Atualização para Swift:
Muitos votos me inspiraram a atualizar esta resposta.
A biblioteca de trechos de código Xcode incorporada possui dispatch_after
apenas o objective-c
idioma. As pessoas também podem criar seu próprio código personalizado de trechos para Swift
.
Escreva isso no 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#>
})
Arraste esse código e solte-o na área da biblioteca de trechos de código.
No final da lista de trechos de código, haverá uma nova entidade chamada My Code Snippet
. Edite isso para um título. Para sugestões ao digitar o Xcode, preencha o Completion Shortcut
.
Para mais informações, consulte CreatingaCustomCodeSnippet .
Arraste esse código e solte-o na área da biblioteca de trechos de código.
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(<#delayInSeconds#>)) {
<#code to be executed after a specified delay#>
}
Expandindo a resposta de Jaime Cham, criei uma categoria NSObject + Blocks como abaixo. Eu senti que esses métodos correspondiam melhor aos performSelector:
métodos NSObject existentes
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 use assim:
[anyObject performBlock:^{
[anotherObject doYourThings:stuff];
} afterDelay:0.15];
delay
deve ser de NSTimeInterval
(que é a double
). #import <UIKit/UIKit.h>
Não é necessário. E não vejo por que - (void)performBlock:(void (^)())block;
poderia ser útil, portanto pode ser removido do cabeçalho.
Talvez seja mais simples do que passar pelo GCD, em uma classe em algum lugar (por exemplo, "Util") ou em uma categoria no objeto:
+ (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];
}
Então, para usar:
[Util runAfterDelay:2 block:^{
NSLog(@"two seconds later!");
}];
Para Swift, criei uma função global, nada de especial, usando o dispatch_after
método Eu gosto mais disso, pois é legível e 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 você pode usar da seguinte maneira:
performBlock({ () -> Void in
// Perform actions
}, afterDelay: 0.3)
after
. Então você pode escrever:after(2.0){ print("do somthing") }
Aqui estão os meus 2 centavos = 5 métodos;)
Eu gosto de encapsular esses detalhes e fazer com que o AppCode me diga como terminar minhas frases.
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 sempre pega um objeto, portanto, para passar argumentos como int / double / float, etc ..... Você pode usar algo como isto.
// NSNumber é um objeto ..
[self performSelector:@selector(setUserAlphaNumber:)
withObject: [NSNumber numberWithFloat: 1.0f]
afterDelay:1.5];
-(void) setUserAlphaNumber: (NSNumber*) number{
[txtUsername setAlpha: [number floatValue] ];
}
Da mesma forma que você pode usar [NSNumber numberWithInt:] etc .... e no método de recebimento, você pode converter o número em seu formato como [number int] ou [number double].
A função dispatch_after despacha um objeto de bloco para uma fila de despacho após um determinado período de tempo. Use o código abaixo para executar algumas tarefas relacionadas à interface do usuário após 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")
})
No 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: {
})
mainQueue,
vez demainQueue)
Aqui está um ajudante útil para evitar fazer a chamada irritante do GCD repetidamente:
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)
}
}
}
Agora você simplesmente adia seu código no thread principal assim:
delay(bySeconds: 1.5) {
// delayed code
}
Se você deseja atrasar seu código para outro segmento :
delay(bySeconds: 1.5, dispatchLevel: .background) {
// delayed code that will run on background thread
}
Se você preferir um Framework que também tenha alguns recursos mais úteis, consulte o HandySwift . Você pode adicioná-lo ao seu projeto via Carthage e usá-lo exatamente como nos exemplos acima:
import HandySwift
delay(bySeconds: 1.5) {
// delayed code
}
No swift 3, podemos simplesmente usar a função DispatchQueue.main.asyncAfter para disparar qualquer função ou ação após o atraso de 'n' segundos. Aqui no código, definimos o atraso após 1 segundo. Você chama qualquer função dentro do corpo dessa função que será acionada após o atraso de 1 segundo.
let when = DispatchTime.now() + 1
DispatchQueue.main.asyncAfter(deadline: when) {
// Trigger the function/action after the delay of 1Sec
}
Você pode agrupar o argumento em sua própria classe ou agrupar a chamada do método em um método que não precise ser passado no tipo primitivo. Em seguida, chame esse método após o seu atraso e, dentro desse método, execute o seletor que deseja executar.
Aqui está como você pode acionar um bloco após um atraso no 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á incluído como uma função padrão no meu repositório .
Swift 3 e Xcode 8.3.2
Este código irá ajudá-lo, eu adiciono uma explicação também
// 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 :)
}
}
}
Acredito que o autor não está perguntando como esperar um tempo fracionário (atraso), mas como passar um escalar como argumento do seletor (withObject :) e a maneira mais rápida no objetivo moderno C é:
[obj performSelector:... withObject:@(0.123123123) afterDelay:10]
seu seletor precisa alterar seu parâmetro para NSNumber e recuperar o valor usando um seletor como floatValue ou doubleValue