7

Estoy tratando de implementar un cronómetro basado en el modelo MVC.Patrón de observador para el cronómetro

El cronómetro utiliza el NSTimer con el selector -(void) tick que se llama cada vez que se agota el tiempo de espera.

He intentado hacer que el cronómetro sea un modelo de reutilización, pero he tenido algunos problemas de diseño con respecto a cómo actualizar el controlador de vista para cada tic.

Primero creé un protocolo con el método tick e hice que el controlador de vista sea su delegado. El controlador de vista luego actualiza las vistas en función de las propiedades del temporizador en cada tic. elapsedTime es un NSTimeInterval de solo lectura.

Funciona, pero estoy pensando que podría ser un mal diseño. Soy un principiante de Objective-C/Cocoa Touch. ¿Debo usar algo como KVO? ¿O hay una solución más elegante para que el modelo notifique al controlador de vista que ha cambiado el elapsedTime?

+1

¡Bonita primera pregunta! Bienvenido a SO! –

+0

¿Cuál es exactamente la relación entre el temporizador y el controlador de vista? ¿El temporizador es propiedad del VC? –

+0

Gracias :) The Timer es propiedad de VC, sí. He implementado un IntervalTimer que hereda de Timer y luego el VC posee el IntervalTimer en su lugar - el IntervalTimer es en realidad el que me da un poco de problemas. – Jach0

Respuesta

0

Últimamente, he estado usando bloques en lugar de viejos @selector. Crea mejor y codifica y mantiene la lógica en la misma ubicación.

No hay bloques de soporte nativo en NSTimer, pero utilizó una categoría de https://gist.github.com/250662/d4f99aa9bde841107622c5a239e0fc6fa37cb179

Sin el selector de cambio, se mantiene el código en un solo lugar:

__block int seconds = 0; 
    NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:1 
               repeats:YES 
               usingBlock:^(NSTimer *timer) { 

               seconds++; 
               // Update UI 

               if (seconds>=60*60*2) { 
                [timer invalidate]; 
               } 




}]; 
5

El temporizador es una buena manera de asegúrese de actualizar su interfaz de usuario periódicamente, pero no la use para controlar el tiempo. NSTimer can drift, y cualquier pequeño error puede acumularse si usa un temporizador para acumular segundos.

En su lugar, use NSTimer para activar un método que actualice su UI, pero obtenga el tiempo real usando NSDate. NSDate le dará una resolución de milisegundos; si realmente necesita algo mejor que eso, considere this suggestion to use Mach's timing functions. Por lo tanto, el uso de NSDate, el código podría ser algo como esto:

- (IBAction)startStopwatch:(id)sender 
{ 
    self.startTime = [NSDate date]; 
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 
                target:self 
               selector:@selector(tick:) 
               userInfo:repeats:YES]; 
} 

- (void)tick:(NSTimer*)theTimer 
{ 
    self.elapsedTime = [self.startTime timeIntervalSinceNow]; 
    [self updateDisplay]; 
} 

- (IBAction)stopStopwatch:(id)sender 
{ 
    [self.timer invalidate]; 
    self.timer = nil; 
    self.elapsedTime = [self.startTime timeIntervalSinceNow]; 
    [self updateDisplay]; 
} 

Su código podría ser un poco más sofisticado si usted permite reiniciar, etc, pero lo importante aquí es que no se está usando a NSTimer medir el tiempo total transcurrido.

Encontrará información adicional útil en this SO thread.

2

Recomendaría en contra de KVO este problema. Introduce mucha complejidad (y varios errores molestos) para obtener pocos beneficios aquí. KVO es importante en los casos en que necesita garantizar una sobrecarga absolutamente mínima. Apple lo usa mucho en casos de objetos de bajo nivel y alto rendimiento como capas. Es la única solución disponible en general que ofrece cero gastos generales cuando no hay observador. La mayoría de las veces, no necesitas eso. Manejar correctamente KVO puede ser complicado, y los errores que puede crear son molestos de rastrear.

No hay nada de malo con su enfoque de delegado. Es correcto MVC. Lo único que realmente necesita preocuparse es que NSTimer no hace grandes promesas sobre cuándo se llama. Incluso se permite saltear un temporizador repetitivo en algunos casos. Para evitar ese problema, generalmente desea calcular elapsedTime en función de la hora actual en lugar de incrementarla. Si el cronómetro puede pausar, entonces necesita mantener un acumulador y una fecha de "cuándo fue la última vez que comencé".

Si necesita temporizadores de mayor precisión o menor costo, puede mirar dispatch_source_set_timer(), pero para un simple cronómetro orientado a humanos, NSTimer está bien, y es una excelente opción para un proyecto simple.

+0

Soy consciente del problema con el temporizador de salto y el uso de incrementos. Estoy usando NSDate y en setElapsedTime comparo NSDates (+ un desplazamiento cuando se utiliza pausa) – Jach0

+0

Estoy tratando de implementar un temporizador de cuenta regresiva de intervalos que hereda del cronómetro y tiene algunos atributos adicionales, como workInterval y restInterval, pero las etiquetas de mi ViewController se actualiza un poco raro. Pensé que podría tener algo que ver con las llamadas secuenciales entre el tick de ViewController, el ticker Timers y el ticker IntervalTimers. – Jach0

Cuestiones relacionadas