NSTimer está programado para ejecutar el runloop. En el código de pregunta, runloop de hilo enviado por GCD no se está ejecutando. Debe iniciarlo manualmente y debe haber una forma de salir del ciclo de ejecución, por lo que debe mantener una referencia al NSTimer e invalidarlo en el momento apropiado.
NSTimer tiene una fuerte referencia al objetivo, por lo que el objetivo no puede tener una referencia fuerte 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, la ejecución del programa de pausa de nuevo en Xcode, se encuentra que el hilo se ha ido.
Por supuesto, los hilos enviados por GCD tienen runloop. GCD genera y reutiliza hilos internamente, allí los hilos son anónimos para la persona que llama. Si no te sientes seguro, podrías usar Thread. No tengas miedo, el código es muy fácil.
En realidad, intento lo mismo la semana pasada y obtengo el mismo error con asker, luego encontré esta página. Intento NSThread antes de darme por vencido. Funciona. Entonces, ¿por qué NSTimer en GCD no puede funcionar? Debería ser. Lee runloop's document para saber cómo funciona NSTimer.
Uso NSThread 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)
}
otra alternativa siguiente segunda respuesta de Kevin, que resuelve mis problemas mediante la sustitución de la NSTimer con MSWeakTimer (github.com/mindsnacks/MSWeakTimer), y acaba de pasar la dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) como el dispatchQueue. –
Gran resumen. Súper gracias. Si entiendo correctamente: 1. ¿Estás diciendo que ningún hilo de envío de GCD tiene un ciclo de ejecución ** excepto ** para el hilo principal? 2. Si es verdadero, ¿qué hay de dispatch_after? ¿Cómo se procesaría sin un runloop? ¿O es que también se implementa en un mecanismo consciente de GCD? – Honey
Los hilos de @Honey Dispatch no tienen bucles ejecutados. El hilo principal no es un hilo de envío (bueno, a menos que estés usando 'dispatch_main()', y si es así, tampoco tiene un runloop, pero presumiblemente no lo estás haciendo). El runloop del subproceso principal se integra con GCD para drenar explícitamente la cola principal como parte del funcionamiento normal del runloop. 'dispatch_after' es parte de GCD (de ahí la palabra" dispatch "en el nombre), no tiene nada que ver con runloops. –