2010-04-26 6 views
8

Remarque: Il est probablement utile de faire défiler la page pour lire mes modifications.Placement d'un NSTimer dans un thread séparé

J'essaie de configurer un NSTimer dans un thread séparé afin qu'il continue à se déclencher lorsque les utilisateurs interagissent avec l'interface utilisateur de mon application. Cela semble fonctionner, mais Leaks signale un certain nombre de problèmes - et je crois que je l'ai réduit à mon code temporel.

Actuellement, updateTimer tente d'accéder à NSArrayController (timersController) qui est lié à NSTableView dans l'interface de mes applications. De là, je prends la première ligne sélectionnée et modifie sa colonne timeSpent. Remarque: le contenu de timersController est une collection d'objets gérés générés via des données de base. A la lecture, je crois que ce que je devrais essayer est d'exécuter la fonction updateTimer sur le thread principal, plutôt que dans mon thread secondaire timers.

Je poste ici dans l'espoir que quelqu'un avec plus d'expérience peut me dire si c'est la seule chose que je fais mal. Ayant lu la documentation d'Apple sur Threading, je l'ai trouvé un domaine extrêmement vaste.

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]; 
} 

MISE À JOUR

Je suis encore totalement perdu en ce qui concerne cette fuite. Je sais que je suis en train de faire quelque chose de mal, mais j'ai dépouillé mon application jusqu'à ses os et je n'arrive toujours pas à le trouver. Pour des raisons de simplicité, j'ai téléchargé mon code de contrôleur d'applications à: a small pastebin. Notez que j'ai maintenant enlevé le code de thread de minuterie et ai plutôt choisi d'exécuter la minuterie dans un looploop séparé (comme suggéré ici).

Si je mets les Fuites Appel Arbre pour cacher les symboles manquants et bibliothèques du système, je me montre la sortie suivante:

EDIT: Liens vers les screenshots brisés et enlevés à cet effet.

+0

Pourquoi configurez-vous une boucle d'exécution supplémentaire? Votre minuterie peut normalement fonctionner correctement avec la boucle d'exécution principale. – zneak

+0

Le lien pastebin est mort. –

Répondre

51

Si la seule raison pour laquelle vous fraye un nouveau thread est de permettre à votre minuterie pour fonctionner pendant que l'utilisateur interagit avec l'interface utilisateur, vous pouvez simplement l'ajouter dans différents modes de runloop:

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

sous forme d'additif cette réponse, il est maintenant possible de programmer des minuteries en utilisant grand Central Dispatch et blocs:

// 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); 

plus tard lorsque le minuteur est nécessaire:

dispatch_source_cancel(_timer); 
+0

C'est une solution beaucoup plus pratique, merci. Je viens de passer un peu de temps à lire sur runloops. Malheureusement, cela ne semble pas aider avec ma fuite. Je l'ai réduit à (je crois) un problème dans la fonction updateTimer. Je crois que cela a quelque chose à voir avec la façon dont je mets à jour la variable timeSpent dans mon NSArray d'objets gérés. – ndg

+0

Si le code que vous avez posté ci-dessus est votre fonction updateTimer, cela me semble correct. Cependant, vous devez faire attention de ne pas déclencher une minuterie une fois que vous l'avez invalidé. – sbooth

+0

C'est en effet ma fonction updateTimer actuelle.J'ai mis à jour le PO pour résumer les problèmes que je vois (et pour donner une mise à jour sur le code utilisé). – ndg

Questions connexes