2010-02-02 1 views
3

J'ai des problèmes pour démarrer & en arrêtant NSTimers. Les docs disent qu'une minuterie est arrêtée par [timer invalidate];Problèmes d'invalidation et de recréation de NSTimer (s)

J'ai un objet timer déclaré en tant que tel

.h 
NSTimer *incrementTimer; 
@property (nonatomic, retain) NSTimer *incrementTimer; 
.m 
@synthesize incrementTimer; 
-(void)dealloc { 
[incrementTimer release]; 
[super dealloc]; 
} 

-La d'habitude.

Quand il est nécessaire, ma méthode effectue les opérations suivantes:

-(void)setGenCount { 
    if(!condition1 && condition2) { 
     incrementTimer = [NSTimer scheduledTimerWithTimeInterval: 2.0 
                 target: self 
                selector:@selector(incrementBatteryVoltage:) 
                userInfo: nil 
                repeats: YES]; 
    } 
} 

Tout fonctionne bien au-dessus. Cependant, une fois que cette minuterie fait son travail, je veux qu'elle s'invalide elle-même. J'invalide le timer car il existe une méthode de décrémentation équivalente qui pourrait être appelée et qui se battrait contre l'incrementTimer si elle était encore active. (Auparavant, j'ai remarqué que mes deux minuteries, si elle est active, ont agi sur le même Ivar en augmentant & diminuant la valeur (une sorte de combat) ... sans se briser) Le sélecteur appelé fonctionne comme suit:

-(void)incrementBatteryVoltage:(NSTimer *)timer { 
    if(battVoltage < 24.0) { 
     generatorDisplay.battVoltage += 0.1; 
     } 
    if(battery1Voltage == 24.0) { 
     [timer invalidate]; 
     } 
    } 

J'ai une méthode égale qui Décrémente le compte de la batterie. (précédemment mentionné)
En raison de ma conception de programme: l'interface simule un affichage de tension. Lorsque la "machine" est éteinte, je veux que toutes les minuteries soient invalidées, quelle que soit la valeur de la tension. Je fais ceci en vérifiant pour voir si le temporisateur est valide.

-(void)deEnergizeDisplays { 

    if([decrementTimer isValid]) { 
     [decrementTimer invalidate]; 
     decrementTimer = nil; 
    } 

    if([incrementTimer isValid]) { 
     [incrementTimer invalidate]; 
     incrementTimer = nil; 
    } 

Je reçois de nombreux plantages "BAD_ACCESS". L'appel de ligne erroné pointe toujours vers mon appel [timer isValid]. Il semble que si le timer est invalidé ... le pointeur n'existe pas non plus. Je sais que le message [timer invalidate] désactive le minuteur, puis il est supprimé de la boucle d'exécution, puis il est libéré. Et ma compréhension est la suivante: c'est un objet autoreleased par son nom de coopération. Mes pensées sont les suivantes: Si j'envoie un message de rétention, la référence ne devrait-elle pas encore exister? J'ai essayé plusieurs combinaisons, ôtant:

timer = nil; 

ou même au lieu de:

if([timer isValid]) 

J'ai essayé:

if([timer != nil]) 

et:

if(timer) 

J'ai toujours obtenir le même accident. Merci pour toute aide sur le démarrage & arrêtant NSTimers.

+0

Rien à voir avec votre accident , Je pense, mais vous ne voulez probablement pas vérifier les nombres à virgule flottante pour l'égalité explicite. –

+0

Comment accompliriez-vous cette tâche? Vérifiez pour (battVoltage> 24)? –

+0

C'est une meilleure façon de faire, oui. –

Répondre

9

MISE À JOUR: Voir Darren's answer. Le problème est que vous n'utilisez pas votre accesseur de propriété lors du réglage des minuteurs.Au lieu de:

incrementTimer = [NSTimer ... 

Vous devriez avoir:

self.incrementTimer = [NSTimer ... 

La syntaxe self.propertyName = ... appellera votre méthode accesseur, et conserver ainsi automatiquement l'objet que vous lui envoyez (depuis votre propriété est définie comme retain). Il suffit d'appeler propertyName = ... pour utiliser l'accesseur de propriété et non. Vous changez simplement la valeur de votre ivar directement.


MISE À JOUR # 2: Après une conversation instructive avec Peter Hosey (voir les commentaires), je l'ai retiré ma suggestion plus tôt à "jamais conserver ou libérer" votre objet de la minuterie. J'ai aussi complètement ré-écrit mon code plus tôt parce que je pense que ce qui suit est une meilleure approche:

Controller.h:

NSTimer *voltageTimer; 
float targetBatteryVoltage; 
... 
@property (nonatomic, retain) NSTimer *voltageTimer; 

Controller.m:

@implementation Controller 
@synthesize voltageTimer; 

- (void)stopVoltageTimer { 
    [voltageTimer invalidate]; 
    self.voltageTimer = nil; 
} 

- (void)setTargetBatteryVoltage:(float)target { 
    [voltageTimer invalidate]; 
    targetBatteryVoltage = target; 
    self.voltageTimer = [NSTimer scheduledTimerWithTimeInterval: 2.0 
           target: self 
           selector: @selector(updateBatteryVoltage:) 
           userInfo: nil 
           repeats: YES]; 
} 

- (void)updateBatteryVoltage:(NSTimer *)timer { 
    const float increment = 0.1; 
    if (abs(battVoltage - targetBatteryVoltage) < increment) { 
     [timer invalidate]; 
    } 
    else if (battVoltage < targetBatteryVoltage) { 
     generatorDisplay.battVoltage += increment; 
    } 
    else if (battVoltage > targetBatteryVoltage) { 
     generatorDisplay.battVoltage -= increment; 
    } 
} 

Maintenant, vous pouvez simplement mettre une tension de batterie cible, et la magie de la minuterie se produira dans les coulisses:

[self setTargetBatteryVoltage:24.0]; 

Votre méthode de mise hors tension se présente comme suit:

- (void)deEnergizeDisplays { 
    [self stopVoltageTimer]; 
} 
+0

PARFAIT. Merci pour l'explication détaillée et la façon alternative de résoudre le problème! –

+1

"Ne jamais conserver ou libérer la minuterie. N'utilisez que «invalidate». »Ne pas le retenir est exactement la raison pour laquelle le questionneur s'est écrasé en essayant de lui envoyer un message; l'objet timer a cessé d'exister lorsque le rappel du timer l'a invalidé. Si un objet en possède un autre, il devrait le conserver; il n'y a pas de bonne raison de faire une exception pour les minuteurs. –

+1

En ce qui concerne la référence circulaire (les temporisateurs conservent/référencent fortement leurs cibles), résolvez cela en ayant tout ce qui détruit cet objet, dites-lui d'arrêter d'abord de surveiller les tensions de la batterie. Dans cette méthode, invalider et laisser partir le minuteur. Cela ouvrira le cercle et vous permettra de détruire l'objet moniteur de tension de batterie. –

3

Vous devez retain la valeur affectée à incrementTimer dans setGenCount. Vous pouvez le faire automatiquement à l'aide de votre propriété synthétisée, qui est accessible via self.:

self.incrementTimer = [NSTimer scheduledTimerWithTimeInterval: ... 
+0

cela se ferait avec: timer = [[NSTimer scheduledTimerWithTimeInterval: 2,0 cible: auto sélecteur: @selector (incrementBatteryVoltage :) userInfo: nil répétitions: OUI] retenir]; –

+0

+1 Bon appel. J'aurais dû voir ce bug plus tôt! –