pero ¿qué pasa si quieres cambiar entre vistas? El código se detendrá, ¿verdad?
Cing
5
@Cing depende de a qué se refiera.
Antzi
1
No olvide que NSTimerretiene su objetivo, por lo que, con esta configuración, si helloWorldTimeres una propiedad self, tiene un ciclo de retención, donde selfretiene helloWorldTimery helloWorldTimerretiene self.
MANIAK_dobrii
@MANIAK_dobrii ¿Puedes comentar también cómo romper el ciclo de retención? Si el VC se desconecta pero el temporizador no se cancela, ¿el ciclo aún está intacto? Entonces, ¿deben cancelar el temporizador y luego cerrar la vista para romper el ciclo?
Dave G
1
Para Swift 4, el método del que desea obtener el selector debe estar expuesto a Objective-C, por lo que el atributo @objc debe agregarse a la declaración del método. por ejemplo, `` `@objc func sayHello () {}` ``
Shamim Hossain
136
Si tiene como objetivo la versión 10 de iOS y superior, puede usar la interpretación basada en bloques de Timer, que simplifica los posibles ciclos de referencia fuertes, por ejemplo:
weakvar timer:Timer?func startTimer(){
timer?.invalidate()// just in case you had existing `Timer`, `invalidate` it before we lose our reference to it
timer =Timer.scheduledTimer(withTimeInterval:60.0, repeats:true){[weakself]_in// do something here
}}func stopTimer(){
timer?.invalidate()}// if appropriate, make sure to stop your timer in `deinit`
deinit{
stopTimer()}
Si bien en Timergeneral es lo mejor, en aras de la integridad, debo señalar que también puede usar el temporizador de envío, que es útil para programar temporizadores en subprocesos en segundo plano. Con los temporizadores de despacho, dado que están basados en bloques, evita algunos de los fuertes desafíos del ciclo de referencia con el antiguo target/ selectorpatrón de Timer, siempre y cuando useweak referencias.
Entonces:
var timer:DispatchSourceTimer?func startTimer(){let queue =DispatchQueue(label:"com.domain.app.timer")// you can also use `DispatchQueue.main`, if you want
timer =DispatchSource.makeTimerSource(queue: queue)
timer!.schedule(deadline:.now(), repeating:.seconds(60))
timer!.setEventHandler {[weakself]in// do whatever you want here
}
timer!.resume()}func stopTimer(){
timer?.cancel()
timer =nil}deinit{self.stopTimer()}
Para obtener más información, consulte la sección Creación de un temporizador de Ejemplos de fuentes de despacho en la sección Fuentes de despacho de la Guía de programación de simultaneidad.
¿Cómo elegiría un temporizador de ejecución única dentro de este enfoque?
Juan Boero
@JuanPabloBoero - No usaría este enfoque para situaciones de fuego único. Usarías dispatch_after. O un no repetitivo NSTimer.
Rob
@Rob Tengo una etiqueta de contador en la pantalla que se actualiza cada segundo desde una función, y estoy usando el código anterior para incrementar mi contador y actualizar el texto de la etiqueta. Pero, el problema al que me enfrento es que mi variable de contador se actualiza cada segundo, pero la pantalla no muestra el último valor de contador en mi UILabel.
Nagendra Rao
Rao, lo anterior es útil para realizar alguna acción en un hilo de fondo. Pero las actualizaciones de la interfaz de usuario deben realizarse en la cola principal. Por lo tanto, programe esto en el hilo principal o al menos envíe las actualizaciones de la interfaz de usuario al hilo principal.
Rob
1
Esta pregunta no pertenece aquí en los comentarios a esta respuesta. Elimine sus comentarios anteriores y publique su propia pregunta. Pero, en resumen, generalmente no mantiene la aplicación ejecutándose en segundo plano, sino que solo hace que parezca que lo estaba. Consulte stackoverflow.com/a/31642036/1271826 .
Rob
17
Aquí hay una actualización de la NSTimerrespuesta, para Swift 3 (en el que NSTimerse cambió el nombre a Timer) usando un cierre en lugar de una función con nombre:
var timer =Timer.scheduledTimer(withTimeInterval:60, repeats:true){(_)in
print("Hello world")}
Si puede permitir un cierto tiempo de deriva, aquí hay una solución simple que ejecuta un código cada minuto:
privatefunc executeRepeatedly(){// put your code here
DispatchQueue.main.asyncAfter(deadline:.now()+60.0){[weakself]inself?.executeRepeatedly()}}
Ejecútelo executeRepeatedly()una vez y se ejecutará cada minuto. La ejecución se detiene cuando selfse libera el objeto propietario ( ). También puede usar una bandera para indicar que la ejecución debe detenerse.
let timer :DispatchSourceTimer=DispatchSource.makeTimerSource(flags:[], queue:DispatchQueue.main)
timer.scheduleRepeating(deadline:.now(), interval:.seconds(60))
timer.setEventHandler
{NSLog("Hello World")}
timer.resume()
Esto es especialmente útil para cuando necesita enviar en una Cola en particular. Además, si planea usar esto para la actualización de la interfaz de usuario, le sugiero que investigue CADisplayLinkya que está sincronizado con la frecuencia de actualización de la GPU.
Respuestas:
Recuerde importar Foundation.
Rápido 4:
fuente
NSTimer
retiene su objetivo, por lo que, con esta configuración, sihelloWorldTimer
es una propiedadself
, tiene un ciclo de retención, dondeself
retienehelloWorldTimer
yhelloWorldTimer
retieneself
.Si tiene como objetivo la versión 10 de iOS y superior, puede usar la interpretación basada en bloques de
Timer
, que simplifica los posibles ciclos de referencia fuertes, por ejemplo:Si bien en
Timer
general es lo mejor, en aras de la integridad, debo señalar que también puede usar el temporizador de envío, que es útil para programar temporizadores en subprocesos en segundo plano. Con los temporizadores de despacho, dado que están basados en bloques, evita algunos de los fuertes desafíos del ciclo de referencia con el antiguotarget
/selector
patrón deTimer
, siempre y cuando useweak
referencias.Entonces:
Para obtener más información, consulte la sección Creación de un temporizador de Ejemplos de fuentes de despacho en la sección Fuentes de despacho de la Guía de programación de simultaneidad.
Para Swift 2, consulte la revisión anterior de esta respuesta .
fuente
dispatch_after
. O un no repetitivoNSTimer
.Aquí hay una actualización de la
NSTimer
respuesta, para Swift 3 (en el queNSTimer
se cambió el nombre aTimer
) usando un cierre en lugar de una función con nombre:fuente
Si puede permitir un cierto tiempo de deriva, aquí hay una solución simple que ejecuta un código cada minuto:
Ejecútelo
executeRepeatedly()
una vez y se ejecutará cada minuto. La ejecución se detiene cuandoself
se libera el objeto propietario ( ). También puede usar una bandera para indicar que la ejecución debe detenerse.fuente
Puedes usar
Timer
(swift 3)En selector () pones el nombre de tu función
fuente
Timer
...NSTimer
ha cambiado de nombreEn Swift 3.0, el GCD se refactorizó:
Esto es especialmente útil para cuando necesita enviar en una Cola en particular. Además, si planea usar esto para la actualización de la interfaz de usuario, le sugiero que investigue
CADisplayLink
ya que está sincronizado con la frecuencia de actualización de la GPU.fuente