sufrir solo sintomas recomendaciones que prevenir paro para mujeres hacer evitar estoy estas empieza cuando como caso cardiaco ataque objective-c cocoa nstimer nsrunloop nsblockoperation

objective-c - solo - recomendaciones para no sufrir un paro cardiaco



¿Cómo creo un NSTimer en un hilo de fondo? (10)

Para swift 3.0,

La respuesta de Tikhonv no explica demasiado. Aquí agrega algo de mi comprensión.

Para hacer las cosas cortas primero, aquí está el código. Es DIFERENTE al código de Tikhonv en el lugar donde creo el temporizador. Creo el temporizador usando constructer y lo agrego al ciclo. Creo que la función scheduleTimer agregará el temporizador al RunLoop del hilo principal. Entonces, es mejor crear un temporizador usando el constructor.

class RunTimer{ let queue = DispatchQueue(label: "Timer", qos: .background, attributes: .concurrent) let timer: Timer? private func startTimer() { // schedule timer on background queue.async { [unowned self] in if let _ = self.timer { self.timer?.invalidate() self.timer = nil } let currentRunLoop = RunLoop.current self.timer = Timer(timeInterval: self.updateInterval, target: self, selector: #selector(self.timerTriggered), userInfo: nil, repeats: true) currentRunLoop.add(self.timer!, forMode: .commonModes) currentRunLoop.run() } } func timerTriggered() { // it will run under queue by default debug() } func debug() { // print out the name of current queue let name = __dispatch_queue_get_label(nil) print(String(cString: name, encoding: .utf8)) } func stopTimer() { queue.sync { [unowned self] in guard let _ = self.timer else { // error, timer already stopped return } self.timer?.invalidate() self.timer = nil } } }

Crear cola

Primero, cree una cola para hacer que el temporizador se ejecute en segundo plano y almacene esa cola como una propiedad de clase para reutilizarla para el cronómetro de detención. No estoy seguro si necesitamos usar la misma cola para comenzar y parar, la razón por la que lo hice es porque vi un mensaje de advertencia here .

La clase RunLoop generalmente no se considera segura para subprocesos y sus métodos solo deben invocarse dentro del contexto del subproceso actual. Nunca intente llamar a los métodos de un objeto RunLoop que se ejecutan en un subproceso diferente, ya que esto podría provocar resultados inesperados.

Así que decidí almacenar la cola y usar la misma cola para el temporizador para evitar problemas de sincronización.

También crea un temporizador vacío y también se almacena en la variable de clase. Hazlo opcional para que puedas detener el temporizador y configurarlo en cero.

class RunTimer{ let queue = DispatchQueue(label: "Timer", qos: .background, attributes: .concurrent) let timer: Timer? }

Start Timer

Para iniciar el temporizador, primero llame a Async desde DispatchQueue. Entonces es una buena práctica verificar primero si el temporizador ya ha comenzado. Si la variable del temporizador no es nula, invalídela () y establézcala en cero.

El siguiente paso es obtener el RunLoop actual. Como hicimos esto en el bloque de cola que creamos, obtendrá RunLoop para la cola de fondo que creamos antes.

Crea el cronómetro Aquí, en lugar de usar scheduleTimer, simplemente llamamos al constructor del temporizador y pasamos la propiedad que desee para el temporizador, como timeInterval, target, selector, etc.

Agregue el temporizador creado a RunLoop. Ejecutarlo.

Aquí hay una pregunta sobre cómo ejecutar RunLoop. De acuerdo con la documentación aquí, dice que efectivamente comienza un bucle infinito que procesa los datos de las fuentes y temporizadores de entrada del bucle de ejecución.

private func startTimer() { // schedule timer on background queue.async { [unowned self] in if let _ = self.timer { self.timer?.invalidate() self.timer = nil } let currentRunLoop = RunLoop.current self.timer = Timer(timeInterval: self.updateInterval, target: self, selector: #selector(self.timerTriggered), userInfo: nil, repeats: true) currentRunLoop.add(self.timer!, forMode: .commonModes) currentRunLoop.run() } }

Temporizador de disparo

Implementa la función de forma normal. Cuando se llama a esa función, se llama debajo de la cola por defecto.

func timerTriggered() { // under queue by default debug() } func debug() { let name = __dispatch_queue_get_label(nil) print(String(cString: name, encoding: .utf8)) }

La función de depuración de arriba se usa para imprimir el nombre de la cola. Si alguna vez te preocupas si se ha estado ejecutando en la cola, puedes llamar para verificar.

Stop Timer

El cronómetro de parada es fácil, llame a validate () y establezca la variable del temporizador almacenada dentro de la clase en cero.

Aquí lo estoy ejecutando debajo de la cola de nuevo. Debido a la advertencia aquí, decidí ejecutar todo el código relacionado con el temporizador debajo de la cola para evitar conflictos.

func stopTimer() { queue.sync { [unowned self] in guard let _ = self.timer else { // error, timer already stopped return } self.timer?.invalidate() self.timer = nil } }

Preguntas relacionadas con RunLoop

De alguna manera estoy un poco confundido sobre si tenemos que detener manualmente el RunLoop o no. De acuerdo con la documentación aquí, parece que cuando no hay temporizadores conectados, saldrá inmediatamente. Entonces, cuando detengamos el temporizador, debería existir por sí mismo. Sin embargo, al final de ese documento, también dijo:

la eliminación de todas las fuentes de entrada y temporizadores conocidos del bucle de ejecución no garantiza que el bucle de ejecución salga. macOS puede instalar y eliminar fuentes de entrada adicionales según sea necesario para procesar las solicitudes dirigidas a la cadena del receptor. Esas fuentes, por lo tanto, podrían evitar que el ciclo de ejecución salga.

Probé la solución a continuación que se proporciona en la documentación para una garantía para finalizar el ciclo. Sin embargo, el temporizador no se dispara después de cambiar .run () al código siguiente.

while (self.timer != nil && currentRunLoop.run(mode: .commonModes, before: Date.distantFuture)) {};

Lo que estoy pensando es que podría ser seguro usar simplemente .run () en iOS. Debido a que la documentación indica que macOS se instala y elimina fuentes de entrada adicionales según sea necesario para procesar las solicitudes dirigidas a la cadena del receptor. Entonces iOS podría estar bien.

Tengo una tarea que debe realizarse cada 1 segundo. Actualmente tengo un NSTimer disparando repetidamente cada 1 segundo. ¿Cómo puedo disparar el temporizador en un hilo de fondo (sin hilo de IU)?

Podría hacer que el NSTimer disparara en el hilo principal y luego usar NSBlockOperation para enviar un hilo de fondo, pero me pregunto si existe una forma más eficiente de hacerlo.


El temporizador debería instalarse en un bucle de ejecución que funciona en una secuencia de fondo ya en ejecución. Ese hilo debería continuar ejecutando el ciclo de ejecución para que el temporizador realmente se active. Y para que ese hilo de fondo continúe siendo capaz de disparar otros eventos del temporizador, necesitaría engendrar un nuevo hilo para manejar los eventos de todos modos (suponiendo, por supuesto, que el procesamiento que está realizando lleve una cantidad significativa de tiempo).

Para lo que sea que valga la pena, creo que manejar los eventos del temporizador generando un nuevo hilo usando Grand Central Dispatch o NSBlockOperation es un uso perfectamente razonable de su hilo principal.


Esto debería funcionar,

Repite un método cada 1 segundo en una cola de fondo sin usar NSTimers :)

- (void)methodToRepeatEveryOneSecond { // Do your thing here // Call this method again using GCD dispatch_queue_t q_background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); double delayInSeconds = 1.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, q_background, ^(void){ [self methodToRepeatEveryOneSecond]; }); }

Si está en la cola principal y desea llamar al método anterior, puede hacerlo para que se convierta en una cola de fondo antes de ejecutar :)

dispatch_queue_t q_background = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0); dispatch_async(q_background, ^{ [self methodToRepeatEveryOneSecond]; });

Espero eso ayude


Hoy después de 6 años, trato de hacer lo mismo, aquí hay una solución alternativa: GCD o NSThread.

Los temporizadores funcionan en conjunción con los bucles de ejecución, el recorrido de un subproceso se puede obtener solo del subproceso, por lo que la clave es ese temporizador de programación en el subproceso.

Excepto runloop del hilo principal, runloop debería comenzar manualmente; debe haber algunos eventos para manejar al ejecutar runloop, como Timer, de lo contrario, runloop saldrá, y podemos usar esto para salir de un runloop si timer es la única fuente de evento: invalidar el temporizador.

El siguiente código es Swift 4:

Solución 0: GCD

weak var weakTimer: Timer? @objc func timerMethod() { // vefiry whether timer is fired in background thread NSLog("It''s called from main thread: /(Thread.isMainThread)") } func scheduleTimerInBackgroundThread(){ DispatchQueue.global().async(execute: { //This method schedules timer to current runloop. self.weakTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerMethod), userInfo: nil, repeats: true) //start runloop manually, otherwise timer won''t fire //add timer before run, otherwise runloop find there''s nothing to do and exit directly. RunLoop.current.run() }) }

Timer tiene una fuerte referencia al objetivo, y runloop tiene una fuerte referencia al temporizador, después de que el temporizador invalide, libere el objetivo, así que mantenga una referencia débil en el objetivo e invalídelo en el momento apropiado para salir del runloop (y luego salir del hilo).

Nota: como optimización, la función de sync de DispatchQueue invoca el bloque en el hilo actual cuando sea posible. En realidad, ejecutas el código anterior en el hilo principal, el temporizador se dispara en el hilo principal, así que no uses la función de sync , de lo contrario el temporizador no se dispara en el hilo que deseas.

Podría nombrar el hilo para rastrear su actividad pausando el programa ejecutándose en Xcode. En GCD, use:

Thread.current.name = "ThreadWithTimer"

Solución 1: hilo

Podríamos usar NSThread directamente. No tengas miedo, el código es fácil.

func configurateTimerInBackgroundThread(){ // Don''t worry, thread won''t be recycled after this method return. // Of course, it must be started. let thread = Thread.init(target: self, selector: #selector(addTimer), object: nil) thread.start() } @objc func addTimer() { weakTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(timerMethod), userInfo: nil, repeats: true) RunLoop.current.run() }

Solución 2: subclase hilo

Si quieres usar la subclase Thread:

class TimerThread: Thread { var timer: Timer init(timer: Timer) { self.timer = timer super.init() } override func main() { RunLoop.current.add(timer, forMode: .defaultRunLoopMode) RunLoop.current.run() } }

Nota: no agregue el temporizador en init; de lo contrario, el temporizador se agrega al runloop de la cadena de llamadas de init, no al runloop de este thread, por ejemplo, ejecuta el siguiente código en thread principal, si TimerThread agrega timer en el método init, el timer se programará en main thread runloop, no timerThread''s runloop. Puede verificarlo en timerMethod() log.

let timer = Timer.init(timeInterval: 1, target: self, selector: #selector(timerMethod), userInfo: nil, repeats: true) weakTimer = timer let timerThread = TimerThread.init(timer: timer) timerThread.start()

PS Acerca de Runloop.current.run() , su documento sugiere que no llame a este método si queremos que runloop finalice, use run(mode: RunLoopMode, before limitDate: Date) , en realidad run() invoque este método de manera repetida en el NSDefaultRunloopMode , ¿qué es el modo? Más detalles en runloop e hilo .


Se timerMethod() mi solución Swift 3.0 para iOS 10+, timerMethod() en cola de fondo.

class ViewController: UIViewController { var timer: Timer! let queue = DispatchQueue(label: "Timer DispatchQueue", qos: .background, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil) override func viewDidLoad() { super.viewDidLoad() queue.async { [unowned self] in let currentRunLoop = RunLoop.current let timeInterval = 1.0 self.timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(self.timerMethod), userInfo: nil, repeats: true) self.timer.tolerance = timeInterval * 0.1 currentRunLoop.add(self.timer, forMode: .commonModes) currentRunLoop.run() } } func timerMethod() { print("code") } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) queue.sync { timer.invalidate() } } }


Si desea que su NSTimer se ejecute incluso en segundo plano, haga lo siguiente:

  1. llame al método [self beginBackgroundTask] en los métodos applicationWillResignActive
  2. llame al método [self endBackgroundTask] en applicationWillEnterForeground

Eso es

-(void)beginBackgroundTask { bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ [self endBackgroundTask]; }]; } -(void)endBackgroundTask { [[UIApplication sharedApplication] endBackgroundTask:bgTask]; bgTask = UIBackgroundTaskInvalid; }


Si necesita esto para que los temporizadores sigan ejecutándose cuando recorra sus vistas (o mapas), debe programarlos en modo de ciclo de ejecución diferente. Reemplace su temporizador actual:

[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];

Con este:

NSTimer *timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES]; [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

Para obtener más información, consulte esta publicación de blog: el seguimiento de eventos detiene NSTimer

EDITAR: segundo bloque de código, el NSTimer todavía se ejecuta en el hilo principal, todavía en el mismo ciclo de ejecución que scrollviews. La diferencia es el modo de ciclo de ejecución. Consulte la publicación del blog para obtener una explicación clara.


Si quiere ir a GCD puro y usar una fuente de despacho, Apple tiene un código de muestra para esto en su Guía de programación de Concurrencia :

dispatch_source_t CreateDispatchTimer(uint64_t interval, uint64_t leeway, dispatch_queue_t queue, dispatch_block_t block) { dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); if (timer) { dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway); dispatch_source_set_event_handler(timer, block); dispatch_resume(timer); } return timer; }

Swift 3:

func createDispatchTimer(interval: DispatchTimeInterval, leeway: DispatchTimeInterval, queue: DispatchQueue, block: @escaping ()->()) -> DispatchSourceTimer { let timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: 0), queue: queue) timer.scheduleRepeating(deadline: DispatchTime.now(), interval: interval, leeway: leeway) // Use DispatchWorkItem for compatibility with iOS 9. Since iOS 10 you can use DispatchSourceHandler let workItem = DispatchWorkItem(block: block) timer.setEventHandler(handler: workItem) timer.resume() return timer }

Luego puede configurar su evento de temporizador de un segundo usando un código como el siguiente:

dispatch_source_t newTimer = CreateDispatchTimer(1ull * NSEC_PER_SEC, (1ull * NSEC_PER_SEC) / 10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // Repeating task });

asegurándose de almacenar y liberar su temporizador cuando haya terminado, por supuesto. Lo anterior te da un margen de 1 / 10th segundo en el disparo de estos eventos, que puedes reforzar si lo deseas.


Swift solamente (aunque probablemente pueda modificarse para usar con Objective-C)

Consulte DispatchTimer de https://github.com/arkdan/ARKExtensions , que "Ejecuta un cierre en la cola de despacho especificada, con intervalos de tiempo especificados, para el número de veces especificado (opcionalmente)."

let queue = DispatchQueue(label: "ArbitraryQueue") let timer = DispatchTimer(timeInterval: 1, queue: queue) { timer in // body to execute until cancelled by timer.cancel() }


class BgLoop:Operation{ func main(){ while (!isCancelled) { sample(); Thread.sleep(forTimeInterval: 1); } } }