无效和重新创建NSTimer的问题

sam*_*u_1 3 cocoa objective-c nstimer

我在启动和停止NSTimers时遇到了问题.文档说[计时器无效]停止计时器;

我有一个声明为这样的计时器对象

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

-通常.

如果需要,我的方法执行以下操作:

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

上面的一切都很好.但是,一旦该计时器完成它的工作,我希望它自己失效.我使计时器无效,因为有一个可以被调用的相等减量方法,如果它仍然是活动的,它将与incrementTimer作对.(之前,我注意到我的两个计时器,如果活跃,通过增加和减少值(一种战斗)在同一个ivar上行动......没有崩溃)调用的选择器如下工作:

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

我有一个减少电池数量的相同方法.(前面提到过)
由于我的程序设计:界面模拟电压显示.当"机器"关闭时,无论电压值是多少,我都希望所有定时器都无效.我这样做是通过检查计时器是否有效来完成的.

-(void)deEnergizeDisplays {

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

    if([incrementTimer isValid]) {
       [incrementTimer invalidate];
       incrementTimer = nil;
    }
Run Code Online (Sandbox Code Playgroud)

我收到了很多"BAD_ACCESS"崩溃.错误的线路呼叫始终指向我的[timer isValid]呼叫.似乎如果定时器无效......指针也不存在.我知道[timer invalidate]消息禁用了计时器,然后它从运行循环中删除然后它被释放.我的理解是:根据它的命名规则,它是一个自动释放的对象.

我的想法是:如果我发送保留消息,那么引用是否仍然存在?我尝试了几种组合,带走了:

timer = nil;
Run Code Online (Sandbox Code Playgroud)

甚至代替:

if([timer isValid])
Run Code Online (Sandbox Code Playgroud)

我试过了 :

if([timer != nil])
Run Code Online (Sandbox Code Playgroud)

和:

if(timer)
Run Code Online (Sandbox Code Playgroud)

我总是得到同样的崩溃.感谢您对启动和停止NSTimers的任何帮助.

e.J*_*mes 9

更新: Darren的回答.问题是您在设置计时器时没有使用属性访问器.代替:

incrementTimer = [NSTimer ...
Run Code Online (Sandbox Code Playgroud)

你应该有:

self.incrementTimer = [NSTimer ...
Run Code Online (Sandbox Code Playgroud)

self.propertyName = ...语法会打电话给你的访问方法,从而自动保留您发送给它(因为你的属性被设置为对象retain).简单地调用propertyName = ...没有使用属性访问器.您只需直接更改ivar的值即可.


更新#2:在与Peter Hosey进行了一次启发性的对话(见评论)之后,我已经删除了我之前的建议,即"永不保留或释放"您的计时器对象.我还完全重写了我之前的代码,因为我认为以下是更好的方法:

Controller.h:

NSTimer *voltageTimer;
float targetBatteryVoltage;
...
@property (nonatomic, retain) NSTimer *voltageTimer;
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以简单地设置目标电池电压,并且计时器魔术将在幕后发生:

[self setTargetBatteryVoltage:24.0];
Run Code Online (Sandbox Code Playgroud)

您的断电方法如下:

- (void)deEnergizeDisplays {
    [self stopVoltageTimer];
}
Run Code Online (Sandbox Code Playgroud)

  • 我冒昧地继续把这个论点放在脑海里,你会很高兴听到我的大脑宣称你是胜利者:)控制器*必须*保持对计时器的引用,以便"deEnergizeDisplays"可以转动它关闭,除非控制器保留计时器,否则无法可靠地工作.控制器*需要*可以访问的计时器对象,即使它已被无效.谢谢你让我直截了当. (2认同)