Лучшее время для аннулирования NSTimer внутри UIViewController, чтобы избежать сохранения цикла - PullRequest
33 голосов
/ 13 августа 2010

Кто-нибудь знает, когда лучше всего остановить NSTimer, который является ссылкой внутри UIViewController, чтобы избежать сохранения цикла между таймером и контроллером?

Вот вопрос более подробно: Iу меня есть NSTimer внутри UIViewController.

Во время ViewDidLoad контроллера представления я запускаю таймер:

statusTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: @selector(updateStatus) userInfo: nil repeats: YES];

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

Теперь я хочу освободить свой контроллер (например, родительский контроллер освобождает его)

вопрос: где я могу поместить вызов [statusTimer invalidate], чтобы заставить таймер освободить ссылку наконтроллер?

Я попытался поместить его в ViewDidUnload, но он не запускается, пока представление не получит предупреждение о памяти, поэтому не очень хорошее место.Я попробовал dealloc, но dealloc никогда не будут вызывать, пока таймер жив (проблема с курицей и яйцом).

Есть хорошие предложения?

Ответы [ 9 ]

21 голосов
/ 25 сентября 2010
  1. Вы могли бы избежать цикла сохранения для начала, например, нацелив таймер на объект StatusUpdate, который содержит не сохраненную (слабую) ссылку на ваш контроллерили StatusUpdater, который инициализируется указателем вашего контроллера, содержит слабую ссылку на него и устанавливает таймер для вас.

    • Вы можете иметьпредставление останавливает таймер в -willMoveToWindow:, когда целевое окно равно nil (которое должно обрабатывать контрпример к -viewDidDisappear:, которое вы предоставили), а также в -viewDidDisappear:.Это означает, что ваш взгляд возвращается в ваш контроллер;вы можете избежать попадания в таймер, просто отправив контроллеру сообщение -view:willMoveToWindow: или отправив уведомление, если вам это нужно.

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

    • Вы можете использоватьнеповторяющийся таймер. Он станет недействительным, как только сработает.Затем вы можете проверить в обратном вызове, должен ли быть создан новый неповторяющийся таймер, и, если это так, создать его.В этом случае нежелательный цикл сохранения будет сохранять только пару таймера и контроллера до следующей даты срабатывания.С 1-й датой пожара вам не о чем беспокоиться.

Каждое предложение, кроме первого, - это способ жить с циклом сохранения исломайте его в подходящее время.Первое предложение фактически избегает цикла сохранения.

6 голосов
/ 18 апреля 2012

Обходной путь - заставить NStimer содержать слабую ссылку на ваш UIViewController. Я создал класс, который содержит слабую ссылку на ваш объект и перенаправляет вызовы на него:

#import <Foundation/Foundation.h>

@interface WeakRefClass : NSObject

+ (id) getWeakReferenceOf: (id) source;

- (void)forwardInvocation:(NSInvocation *)anInvocation;

@property(nonatomic,assign) id source;

@end

@implementation WeakRefClass

@synthesize source;

- (id)init{
    self = [super init];
//    if (self) {
//    }
    return self;
}

+ (id) getWeakReferenceOf: (id) _source{

    WeakRefClass* ref = [[WeakRefClass alloc]init];
    ref.source = _source; //hold weak reference to original class

    return [ref autorelease];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [[self.source class ] instanceMethodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    [anInvocation    invokeWithTarget:self.source ];

}

@end

и вы используете это так:

statusTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: [WeakRefClass getWeakReferenceOf:self] selector: @selector(updateStatus) userInfo: nil repeats: YES];

Ваш метод dealloc вызывается (в отличие от ранее), и внутри него вы просто вызываете:

[statusTimer invalidate];
3 голосов
/ 13 августа 2010

Вы можете попробовать с - (void)viewDidDisappear:(BOOL)animated, а затем вам нужно еще раз подтвердить его в - (void)viewDidAppear:(BOOL)animated

Подробнее здесь

1 голос
/ 05 октября 2013

недействительный таймер внутри - (void) viewWillDisappear: (BOOL) анимированные работали для меня

1 голос
/ 13 августа 2010

Метод -viewDidDisappear может быть тем, что вы ищете. Он вызывается всякий раз, когда представление скрыто или отклонено.

0 голосов
/ 15 августа 2012

Если для timer.REPEAT установлено значение YES, владелец таймера (например, контроллер представления или представление) не будет освобожден до тех пор, пока таймер не станет недействительным.

Решение этой проблемывопрос в том, чтобы найти какую-нибудь точку запуска, чтобы остановить ваш таймер.

Например, я запускаю таймер для воспроизведения анимированных изображений GIF в виде, и точка запуска будет:

  1. когда представление добавляется в суперпредставление, запускается таймер
  2. , когда представление удаляется из суперпредставления, останавливается таймер

, поэтому я выбираю UIView 's willMoveToWindow: метод как таковой:

- (void)willMoveToWindow:(UIWindow *)newWindow {
    if (self.animatedImages && newWindow) {
        _animationTimer = [NSTimer scheduledTimerWithTimeInterval:_animationInterval
            target:self selector:@selector(drawAnimationImages)
            userInfo:nil repeats:YES];
    } else {
        [_animationTimer invalidate];
        _animationTimer = nil;
    }
}

Если ваш таймер принадлежит ViewController, возможно, viewWillAppear: и viewWillDisappear: являются хорошим местом для запуска и остановки таймера.

0 голосов
/ 13 июня 2011

У меня была точно такая же проблема, и в конце я решил переопределить метод выпуска View Controller, чтобы найти особый случай, когда retainCount равен 2, и мой таймер работает. Если таймер не работал, это привело бы к тому, что счетчик выпусков упал до нуля, а затем вызвал dealloc.

- (oneway void) release {
    // Check for special case where the only retain is from the timer
    if (bTimerRunning && [self retainCount] == 2) {
        bTimerRunning = NO;
        [gameLoopTimer invalidate];
    }
    [super release];
}

Я предпочитаю этот подход, потому что он делает его простым и инкапсулированным в одном объекте, то есть в View Controller, и, следовательно, его легче отлаживать. Мне не нравится, однако, дурачиться с цепочкой удержания / выпуска, но я не могу найти способ обойти это.

Надеюсь, что это поможет, и если вы найдете лучший подход, я бы тоже хотел это услышать.

Dave

РЕДАКТИРОВАТЬ: Должен был - (односторонний аннулируется)

0 голосов
/ 09 октября 2010

Вы можете написать этот код в функции dealloc контроллера вида

например,

-(void)dealloc
{
   if([statusTimer isValid])
  {
       [statusTimer inValidate];
       [statustimer release];
      statusTimer = nil;
  }
}

таким образом счетчик ссылок statustimer будет автоматически уменьшаться на 1 а также данные на выделенной памяти также сотрутся

также вы можете написать этот код в - (void)viewDidDisappear:(BOOL)animated function

0 голосов
/ 13 августа 2010

Я написал класс "слабой ссылки" именно по этой причине.Он подклассов NSObject, но перенаправляет все методы, которые NSObject не поддерживает целевой объект.Таймер сохраняет слабую ссылку, но слабая не сохраняет свою цель, поэтому нет цикла сохранения.

Цель вызывает [слабый очистить] и [недействительный таймер] или около того в dealloc.Разве не так?

(Следующая очевидная вещь - написать свой собственный класс таймера, который обрабатывает все это для вас.)

...