ios objective-c grand-central-dispatch nstimer nsrunloop

ios - Ejecutar repitiendo NSTimer con GCD?



objective-c grand-central-dispatch (3)

Esta es una mala idea Estaba a punto de eliminar esta respuesta, pero la dejé aquí para evitar que otros cometan el mismo error que yo. Gracias #Kevin_Ballard por señalar esto.

Solo agregarías una línea a tu ejemplo y funcionaría tal como lo escribiste:

[[NSRunLoop currentRunLoop] run]

por lo que obtendrías

-(void)viewDidLoad{ [super viewDidLoad]; myQueue = dispatch_queue_create("someDescription", NULL); dispatch_async(myQueue, ^{ [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(runTimer) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] run] }); }

Como su cola myQueue contiene un NSThread y contiene un NSRunLoop y como el código en el dispatch_async se ejecuta en el contexto de ese NSThread, currentRunLoop devolverá un bucle de ejecución detenido asociado al hilo de su cola.

Me preguntaba por qué, cuando creas un temporizador de repetición en un bloque GCD, ¿no funciona?

Esto funciona bien:

-(void)viewDidLoad{ [super viewDidLoad]; [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(runTimer) userInfo:nil repeats:YES]; } -(void)runTimer{ NSLog(@"hi"); }

Pero esto no funciona:

dispatch_queue_t myQueue; -(void)viewDidLoad{ [super viewDidLoad]; myQueue = dispatch_queue_create("someDescription", NULL); dispatch_async(myQueue, ^{ [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(runTimer) userInfo:nil repeats:YES]; }); } -(void)runTimer{ NSLog(@"hi"); }


NSTimer está programado para enlazar runloop. En el código de pregunta, no se está ejecutando el bucle de ejecución del subproceso enviado por GCD. Debe iniciarlo manualmente y debe haber una manera de salir del ciclo de ejecución, por lo que debe mantener una referencia al NSTimer e invalidarla en el momento adecuado.

NSTimer tiene una fuerte referencia al objetivo, por lo que el objetivo no puede tener una fuerte referencia al temporizador, y Runloop tiene una fuerte referencia al temporizador.

weak var weakTimer: Timer? func configurateTimerInBackgroundThread(){ DispatchQueue.global().async { // Pause program execution in Xcode, you will find thread with this name Thread.current.name = "BackgroundThreadWithTimer" // This timer is scheduled to current run loop self.weakTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(runTimer), userInfo: nil, repeats: true) // Start current runloop manually, otherwise NSTimer won''t fire. RunLoop.current.run(mode: .defaultRunLoopMode, before: Date.distantFuture) } } @objc func runTimer(){ NSLog("Timer is running in mainThread: /(Thread.isMainThread)") }

Si el temporizador se invalida en el futuro, vuelva a pausar la ejecución del programa en Xcode, encontrará que el hilo se ha ido.

Por supuesto, los hilos enviados por GCD tienen runloop. GCD genera y reutiliza subprocesos internamente, los subprocesos son anónimos para el llamante. Si no te sientes seguro, puedes usar Thread. No tengas miedo, el código es muy fácil.

En realidad, intenté lo mismo la semana pasada y obtengo el mismo error con el autor de la pregunta, luego encontré esta página. Intento NSThread antes de rendirme. Funciona. Entonces, ¿por qué NSTimer en GCD no puede funcionar? Debería ser. Lea el documento de runloop para saber cómo funciona NSTimer.

Use NSThread para trabajar con NSTimer:

func configurateTimerInBackgroundThread(){ let thread = Thread.init(target: self, selector: #selector(addTimerInBackground), object: nil) thread.name = "BackgroundThreadWithTimer" thread.start() } @objc func addTimerInBackground() { self.weakTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(runTimer), userInfo: nil, repeats: true) RunLoop.current.run(mode: .defaultRunLoopMode, before: Date.distantFuture) }


NSTimers están programados en el bucle de ejecución del hilo actual. Sin embargo, los subprocesos de distribución de GCD no tienen bucles de ejecución, por lo que la programación de temporizadores en un bloque GCD no va a hacer nada.

Hay tres alternativas razonables:

  1. Averigüe en qué bucle de ejecución desea programar el temporizador y explícitamente. Use +[NSTimer timerWithTimeInterval:target:selector:userInfo:repeats:] para crear el temporizador y luego -[NSRunLoop addTimer:forMode:] para programarlo en el ciclo de ejecución que desea usar. Esto requiere tener un identificador en el bucle de ejecución en cuestión, pero solo puede usar +[NSRunLoop mainRunLoop] si desea hacerlo en el hilo principal.
  2. Cambie a utilizar una fuente de despacho basada en temporizador. Esto implementa un temporizador en un mecanismo compatible con GCD, que ejecutará un bloque en el intervalo que desee en la cola de su elección.
  3. Explícitamente dispatch_async() volver a la cola principal antes de crear el temporizador. Esto es equivalente a la opción # 1 usando el bucle de ejecución principal (ya que también creará el temporizador en el hilo principal).

Por supuesto, la verdadera pregunta aquí es, ¿para qué estás creando un temporizador a partir de una cola de GCD?