2010-04-26 19 views
8

Nota: Probablemente valga la pena desplazarse hacia abajo para leer mi edición.Colocación de un NSTimer en un hilo separado

Estoy tratando de configurar un NSTimer en un hilo separado para que continúe encendiéndose cuando los usuarios interactúen con la interfaz de usuario de mi aplicación. Esto parece funcionar, pero Leaks informa una serie de problemas, y creo que lo he reducido a mi código de temporizador.

Actualmente, lo que está sucediendo es que updateTimer intenta acceder a un NSArrayController (timersController) que está vinculado a un NSTableView en mi interfaz de aplicaciones. A partir de ahí, tomo la primera fila seleccionada y modifico su columna timeSpent. Nota: el contenido de timersController es una colección de objetos gestionados generados a través de Core Data.

Al leer todo, creo que lo que debería intentar es ejecutar la función updateTimer en el hilo principal, en lugar de en el hilo secundario de mis temporizadores.

Estoy publicando aquí con la esperanza de que alguien con más experiencia pueda decirme si eso es lo único que estoy haciendo mal. Después de leer la documentación de Apple sobre Threading, creo que es un área temática abrumadoramente grande.

NSThread *timerThread = [[[NSThread alloc] initWithTarget:self selector:@selector(startTimerThread) object:nil] autorelease]; 
[timerThread start]; 

-(void)startTimerThread 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; 
    activeTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTimer:) userInfo:nil repeats:YES] retain]; 

    [runLoop run]; 
    [pool release]; 
} 
-(void)updateTimer:(NSTimer *)timer 
{ 
    NSArray *selectedTimers = [timersController selectedObjects]; 
    id selectedTimer = [selectedTimers objectAtIndex:0]; 
    NSNumber *currentTimeSpent = [selectedTimer timeSpent]; 

    [selectedTimer setValue:[NSNumber numberWithInt:[currentTimeSpent intValue]+1] forKey:@"timeSpent"]; 
} 
-(void)stopTimer 
{ 
    [activeTimer invalidate]; 
    [activeTimer release]; 
} 

ACTUALIZACIÓN

Todavía estoy totalmente perdido con respecto a esta filtración. Sé que evidentemente estoy haciendo algo mal, pero he reducido mi aplicación a sus mínimos y todavía no puedo encontrarla. Para simplificar, he cargado mi código de controlador de aplicaciones a: a small pastebin. Tenga en cuenta que ahora eliminé el código del thread del temporizador y opté por ejecutar el temporizador en un runloop separado (como se sugiere aquí).

Si fijo las fugas de llamadas en árbol para ocultar ambos símbolos perdidos y bibliotecas del sistema, estoy muestra el siguiente resultado:

EDIT: Enlaces a imágenes rotas y por lo tanto eliminado.

+0

¿Por qué configura un bucle de ejecución adicional? Su temporizador normalmente puede funcionar bien con el ciclo de ejecución principal. – zneak

+0

El enlace pastebin está muerto. –

Respuesta

51

Si la única razón por la que usted está lanzando un nuevo hilo es permitir que su temporizador para ejecutar mientras el usuario está interactuando con la interfaz de usuario puede simplemente añadir que en diferentes modos Runloop:

NSTimer *uiTimer = [NSTimer timerWithTimeInterval:(1.0/5.0) target:self selector:@selector(uiTimerFired:) userInfo:nil repeats:YES];  
[[NSRunLoop mainRunLoop] addTimer:uiTimer forMode:NSRunLoopCommonModes]; 

Como una adición al esta respuesta ahora es posible programar los temporizadores utilizando Grand central Dispatch y bloques:

// Update the UI 5 times per second on the main queue 
// Keep a strong reference to _timer in ARC 
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); 
dispatch_source_set_timer(_timer, DISPATCH_TIME_NOW, (1.0/5.0) * NSEC_PER_SEC, 0.25 * NSEC_PER_SEC); 

dispatch_source_set_event_handler(_timer, ^{ 
    // Perform a periodic action 
}); 

// Start the timer 
dispatch_resume(_timer); 

más tarde, cuando el temporizador ya no es necesaria:

dispatch_source_cancel(_timer); 
+0

Esa es una solución mucho más práctica, gracias. Acabo de pasar un tiempo leyendo en runloops. Desafortunadamente, no parece ayudar con mi fuga. Lo he reducido a (creo) un problema en la función updateTimer. Creo que tiene algo que ver con la forma en que estoy actualizando la variable timeSpent dentro de mi NSArray de objetos gestionados. – ndg

+0

Si el código que publicó anteriormente es su función de actualización actual del temporizador, me parece correcto. Sin embargo, debes tener cuidado de no liberar un temporizador una vez que lo hayas invalidado. – sbooth

+0

Esa es de hecho mi función actual updateTimer.Actualicé el OP para resumir los problemas que estoy viendo (y para recibir una actualización sobre el código utilizado). – ndg

Cuestiones relacionadas