Problems invalidating & re-creating NSTimer(s)
cocoa, nstimer, objective-c
Solution
UPDATE: See Darren's answer. The problem is that you are not using your property accessor when setting the timers. Instead of:
incrementTimer = [NSTimer ...
You should have:
self.incrementTimer = [NSTimer ...
The `self.propertyName = ...` syntax will call your accessor method, and thereby automatically retain the object that you send to it (since your property is set up as `retain`). Simply calling `propertyName = ...` does not use the property accessor. You are simply changing the value of your ivar directly.
UPDATE #2: After an enlightening conversation with Peter Hosey (see comments), I have removed my earlier suggestion to "never retain or release" your timer object. I have also completely re-written my earlier code because I think the following is a better approach:
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;
}
}
Now, you can simply set a target battery voltage, and the timer magic will happen behind the scenes:
[self setTargetBatteryVoltage:24.0];
Your power-off method would look as follows:
- (void)deEnergizeDisplays {
[self stopVoltageTimer];
}
Problem
I'm having problems starting & stopping NSTimers. The docs say that a timer is stopped by [timer invalidate]; I have a timer object declared as such ``` .h NSTimer *incrementTimer; @property (nonatomic, retain) NSTimer *incrementTimer; .m @synthesize incrementTimer; -(void)dealloc { [incrementTimer release]; [super dealloc]; } ``` -The usual. When it's needed, my method does the following: ``` -(void)setGenCount { if(!condition1 && condition2) { incrementTimer = [NSTimer scheduledTimerWithTimeInterval: 2.0 target: self selector:@selector(incrementBatteryVoltage:) userInfo: nil repeats: YES]; } } ``` Everything above works fine. However, once that timer does it's job, I want it to invalidate itself. I invalidate the timer because there is an equal decrement method that could be called and would fight against the incrementTimer if it was still active. (Previously, I noticed that my two timers, if active, were acting on the same ivar by increasing & decreasing the value (a sort of fight)... without crashing) The selector called works as follows: ``` -(void)incrementBatteryVoltage:(NSTimer *)timer { if(battVoltage < 24.0) { generatorDisplay.battVoltage += 0.1; } if(battery1Voltage == 24.0) { [timer invalidate]; } } ``` I have an equal method that Decrements the battery count. (previously mentioned) Due to my program design: the interface simulates a voltage display. When the "machine" is turned off, I want all the timers invalidated, regardless of what any voltage value is. I'm doing this by checking to see if the timer is valid. ``` -(void)deEnergizeDisplays { if([decrementTimer isValid]) { [decrementTimer invalidate]; decrementTimer = nil; } if([incrementTimer isValid]) { [incrementTimer invalidate]; incrementTimer = nil; } ``` I'm getting numerous "BAD_ACCESS" crashes. The erroneous line call is always pointing toward my [timer isValid] call. It seems that if the timer is invalidated... the pointer doesn't exist either. I know that the [timer invalidate] message disables the timer and then it is removed from the run loop and then it is released. And my understanding is: it is an autoreleased object per it's naming covention. My thought are: If I'm sending a retain message, shouldn't the reference still exist? I've tried several combinations, taking away: ``` timer = nil; ``` or even instead of: ``` if([timer isValid]) ``` I tried : ``` if([timer != nil]) ``` and: ``` if(timer) ``` I always get the same crash. Thanks for any help on starting & stopping NSTimers.