Проблемы аннулирования и воссоздания NSTimer (s) - PullRequest
3 голосов
/ 03 февраля 2010

У меня проблемы с запуском и остановкой NSTimers. В документах говорится, что таймер остановлен с помощью [timer invalidate];

У меня есть объект таймера, объявленный таким образом

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

- обычный.

Когда это необходимо, мой метод выполняет следующие действия:

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

Все выше работает отлично. Однако, как только этот таймер выполнит свою работу, я хочу, чтобы он аннулировал себя. Я лишил законной силы таймер, потому что существует метод равного декремента, который можно было бы вызывать и который боролся бы с incrementTimer, если бы он все еще был активным. (Ранее я заметил, что два моих таймера, если они были активны, действовали на один и тот же ивар, увеличивая и уменьшая значение (своего рода бой) ... без сбоев) Вызываемый селектор работает следующим образом:

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

У меня есть такой же метод, который уменьшает количество батарей. (упомянуто ранее)
Благодаря дизайну моей программы: интерфейс имитирует отображение напряжения. Когда «машина» выключена, я хочу, чтобы все таймеры были отключены, независимо от того, какое значение напряжения. Я делаю это, проверяя, действителен ли таймер.

-(void)deEnergizeDisplays {

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

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

Я получаю многочисленные "BAD_ACCESS" сбои. Ошибочный вызов линии всегда указывает на мой вызов [timer isValid]. Кажется, что если таймер признан недействительным ... указатель тоже не существует Я знаю, что сообщение [timer invalidate] отключает таймер, а затем удаляется из цикла выполнения и затем освобождается. И мое понимание таково: это автоматически выпущенный объект в соответствии с соглашением об именах.

Я подумала: если я отправляю сообщение сохранения, разве ссылка не должна существовать? Я пробовал несколько комбинаций, убирая:

timer = nil;

или даже вместо:

if([timer isValid])

Я пытался:

if([timer != nil])

и:

if(timer)

У меня всегда один и тот же сбой. Спасибо за любую помощь по запуску и остановке NSTimers.

Ответы [ 2 ]

9 голосов
/ 03 февраля 2010

ОБНОВЛЕНИЕ: См. Ответ Даррена . Проблема в том, что вы не используете средство доступа к свойству при настройке таймеров. Вместо:

incrementTimer = [NSTimer ...

Вы должны иметь:

self.incrementTimer = [NSTimer ...

Синтаксис self.propertyName = ... вызовет ваш метод доступа и, таким образом, автоматически сохранит объект, который вы отправляете ему (поскольку ваше свойство установлено как retain). Простой вызов propertyName = ... действительно не использует средство доступа к свойству. Вы просто напрямую изменяете стоимость своего ивара.


ОБНОВЛЕНИЕ № 2: После содержательного разговора с Питером Хоси (см. Комментарии) я удалил свое предыдущее предложение «никогда не сохранять и не отпускать» объект таймера. Я также полностью переписал свой предыдущий код, потому что я думаю, что следующий подход является лучшим:

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

Теперь вы можете просто установить целевое напряжение батареи, и тайма магии будет происходить за кулисами:

[self setTargetBatteryVoltage:24.0];

Ваш метод отключения будет выглядеть следующим образом:

- (void)deEnergizeDisplays {
    [self stopVoltageTimer];
}
3 голосов
/ 03 февраля 2010

Вам необходимо retain значение, присвоенное incrementTimer в setGenCount. Вы можете сделать это автоматически, используя ваше синтезированное свойство, доступ к которому осуществляется через self.:

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