Подтвердить что ты не робот

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

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

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

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

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

Приведенное выше приводит к тому, что таймер удерживает ссылку на контроллер вида.

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

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

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

Любые хорошие предложения?

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

Один из способов - сделать 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

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

Целевой вызов [weakref clear] и [timer invalidate] или так в dealloc. Икки, не правда ли?

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

Ответ 7

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

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

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

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

поэтому я выбираю метод UIView 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: - это хорошее место для запуска и остановки таймера.

Ответ 8

У меня была точно такая же проблема, и в конце я решил переопределить метод release View Controller, чтобы найти специальный случай saveCount, равный 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

РЕДАКТИРОВАТЬ: Должно быть - (oneway void)

Ответ 9

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

например,

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

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

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