Сохранение повторяющегося NSTimer для последующего доступа? - PullRequest
0 голосов
/ 08 февраля 2011

Я создаю NSTimer в методе createTimer, к которому я хочу обратиться в более позднем методе cancelTimer.Чтобы облегчить это, я вступаю во владение NSTimer через оставленное свойство, чтобы я мог вернуться к нему позже.Меня смущает проблема: если я запускаю таймер, отменяю его и запускаю снова, код вылетает.

@property(nonatomic, retain) NSTimer *walkTimer;

.

-(void)createTimer {
    NSTimer *tempTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateTimerDisplay) userInfo:nil repeats:YES];
    [self setWalkTimer:tempTimer];
}

-(void)cancelTimer {
    [walkTimer release];
    [[self walkTimer] invalidate];
}

Теперь, кажется, я это исправилизменив cancelTimer на:

-(void)cancelTimer {
    [self setWalkTimer:nil];
    [[self walkTimer] invalidate];
}

Мне просто любопытно, почему не работает релиз, мое понимание таково:

  1. NSTimer (объект автоматического выпуска, не принадлежащий мне)
  2. setWalkTimer (вступает во владение мной, retainCount + 1)
  3. выпуск (отказывается от владения, retainCount-1)
  4. аннулировать (позволяет системе избавляться от таймера)

РЕДАКТИРОВАТЬ:

// this fails ...
-(void)cancelTimer {
    [[self walkTimer] invalidate];
    [walkTimer release];
}

// this works fine ...
-(void)cancelTimer {
    [[self walkTimer] invalidate];
    [self setWalkTimer: nil];
}

РЕДАКТИРОВАТЬ: 002

Первоначально я думаю, что я перепутал

@property(nonatomic, retain) NSTimer *walkTimer;
// &
[self setWalkTimer];

и думая, что мне нужен релиз для балансировки свойства, я не перезаписываю его новым набором (либо другому объекту, либо nil) и, наконец, освобождаю свойство в dealloc.

Является лисобственность (сохранить) так же, как сохранить, я бы сказал нет, я думаю, что именно там я ошибался.

РЕДАКТИРОВАТЬ: 003 Что касается этого вопроса, я думаю, что я лично запутал вещи, неправильно используя [walkTimer release] В результате тема перешла к принципиально новому вопросу, который я записал как это

Ответы [ 3 ]

4 голосов
/ 08 февраля 2011

Вы release, прежде чем позвонить invalidate. Это означает, что к тому времени, когда вы звоните invalidate, вы уже отказались от владения таймером. На практике вы заканчиваете тем, что вызываете invalidate для освобожденного экземпляра таймера.

Что вы должны сделать, это позвонить invalidate до , когда вы позвоните release. Поскольку вы используете сохраняемое свойство, вы можете просто установить для свойства nil:

// Schedule the timer.
self.walkTimer = [NSTimer scheduledTimerWith...];

// Cancel the timer.
[self.walkTimer invalidate];
self.walkTimer = nil;

Обновление, чтобы устранить любую путаницу в отношении управления памятью

Важно помнить о Правилах управления памятью из Objective-C - вы владеете объектом, если вы вызываете alloc, copy или retain на нем, и если вы владеете объектом объект, вы должны в конечном итоге позвонить release. В этом случае setWalkTimer: сохраняет таймер, потому что свойство объявлено как retain - это означает, что вы владеете таймером и должны вызвать release в будущем. Метод invalidate не считается отказом от владения таймером.

Когда вы планируете таймер, цикл выполнения сохраняет его, а когда таймер запускается или становится недействительным, цикл запуска освобождает его. Но на самом деле вам не нужно это знать - это деталь реализации. Вызов release по invalidate предназначен только для балансировки retain, когда таймер был запланирован в цикле выполнения.

0 голосов
/ 09 февраля 2011

Не сохраняйте запланированный NSTimer, если вы установили его цель на себя Не устанавливайте self в качестве цели повторяющегося таймера, если вы не уверены, что знаете все последствия

(... иначе среда выполнения утопит котенка в утечки таймеров, целей и пользовательских данных - или так гласит поговорка.)

Пожалуйста, прочитайте и перечитайте «Обзор» в Справочнике классов NSTimer и обратите особое внимание на последний абзац.

В двух словах:

  1. Если вы запланируете NSTimer, он станет связанным с текущим циклом выполнения, который сохраняет it.
  2. Кроме того, таймер сохраняет свою цель .
  3. NSTimer экземпляры не могут быть использованы повторно : «После того, как объекты таймера станут недействительными, их нельзя будет использовать повторно» .

Так что нет смысла сохранять запланированный таймер в первую очередь.

Если вам нужно держаться за него (например, чтобы отменить его), используйте не владеющий (он же слабый) ссылку на него.

Обновление:
Подробное объяснение см. В моем ответе на другой вопрос (теперь в нем есть графики - хотя и только в виде ссылок - и прочее).

Пожалуйста, рассмотрите оставшуюся часть этого поста (а также многие мои комментарии) как устаревшие.


Ваша собственность становится

@property (nonatomic, assign) NSTimer *walkTimer;

КСТАТИ:

-(void)cancelTimer {
    [self setWalkTimer:nil]; // great, now [self walkTimer] returns nil so
    [[self walkTimer] invalidate]; // here, you are calling [nil invalidate]
}

И поскольку в Objective C абсолютно нет сообщений, ваш сбой чудесным образом исчезает ... пока ваш таймер с радостью продолжит срабатывать.

Редактировать

Я забыл упомянуть:
Таймер хочет селектор, который принимает один аргумент, который будет таймером, который сработал ... Или это просто опечатка?

0 голосов
/ 08 февраля 2011

Вы должны сделать недействительным, прежде чем отпустить. После того, как таймер сработал, вы единственный, кто удерживает таймер. Поэтому, когда вы вызываете релиз, таймер освобождается. Затем вы вызываете «invalidate» в недействительной памяти и вылетаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...