NSTimers вызывает утечки - PullRequest
1 голос
/ 02 марта 2011

Я много читал о NSTimers, но, должно быть, с ними что-то не так, потому что практически все утечки обнаруживаются в инструменте утечки. В столбце «Ответственный кадр» написано - [NSCFTimer или + [NSTimer (NSTimer).

Итак, вот как я настроил NSTimer в моем главном меню. Я сократил его, чтобы показать, как настроен таймер.

.h -

@interface MainMenu : UIView {
    NSTimer *timer_porthole;    
}

@end


@interface MainMenu ()

-(void) onTimer_porthole:(NSTimer*)timer;


@end

.m -

(в initWithFrame)

- (id)initWithFrame:(CGRect)frame {

    self = [super initWithFrame:frame];
    if (self) {

        timer_porthole = [[NSTimer scheduledTimerWithTimeInterval:.05
                                                           target:self
                                                         selector:@selector(onTimer_porthole:)
                                                         userInfo:nil
                                                          repeats:YES] retain];

    }
    return self;
}   

При выходе из вида убивает таймеры:

-(void) kill_timers{
     [timer_porthole invalidate];
     timer_porthole=nil;
}

И, конечно же, dealloc:

- (void)dealloc {
    [timer_porthole invalidate];
    [timer_porthole release];
    timer_porthole = nil;

    [super dealloc];
}

Ответы [ 3 ]

4 голосов
/ 02 марта 2011

Не звоните удерживать на своем NSTimer!

Я знаю, что это звучит нелогично, но когда вы создаете экземпляр, он автоматически регистрируется в текущем (вероятном главном) цикле выполнения потоков (NSRunLoop). Вот что Apple должна сказать по этому вопросу ...

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

После того, как запланировано на цикл выполнения, таймер срабатывает с заданным интервалом пока это не признано недействительным. неповторяющийся таймер аннулирует себя сразу после того, как он выстрелил. Тем не мение, для повторяющегося таймера вы должны лишить законной силы объект таймера вызвав его недействительный метод. Вызов этого метода запрашивает снятие таймера с текущего цикл запуска; в результате вы должны всегда вызывать метод invalidate из тот же поток, на котором был таймер установлены. Аннулирование таймера немедленно отключает его, чтобы он не дольше влияет на цикл выполнения. Бег цикл затем удаляет и освобождает таймер, либо непосредственно перед метод invalidate возвращает или при некотором позже точка. После признания недействительным таймер объекты не могут быть использованы повторно.

Итак, твое воплощение становится ...

timer_porthole = [NSTimer scheduledTimerWithTimeInterval:.05
                                                           target:self
                                                       selector:@selector(onTimer_porthole:)
                                                         userInfo:nil
                                                          repeats:YES];

И теперь, когда вы больше не держите ссылку на экземпляр, вам не нужен вызов release в вашем методе dealloc.

3 голосов
/ 03 марта 2011

Я видел, что вы уже приняли ответ, но есть две вещи, которые я хотел исправить:

  1. не требуется для сохранения запланированного таймера, но это нене навреди (пока вы отпускаете его, когда он больше не нужен).«Проблемная» часть отношения таймер / цель состоит в том, что ...
  2. таймер сохраняет свою цель .И вы решили установить эту цель на self.
    Это означает - сохранен или нет - таймер будет поддерживать ваш объект живым, пока он действителен.

Спомните, что давайте пересмотрим ваш код снизу вверх:

- (void)dealloc {
    [timer_porthole invalidate]; // 1
    [timer_porthole release];
    timer_porthole = nil;  // 2

    [super dealloc];
}

1 не имеет смысла:
Если timer_porthole все еще был действительным таймером (т.е. запланирован на этапе выполнения) он сохранит ваш объект, поэтому этот метод не будет вызван в первую очередь ...

2 здесь тоже нет смысла:
Это dealloc!Когда [super dealloc] вернется, память, занятая вашим экземпляром в куче, будет освобождена.Конечно, вы можете обнулить свою часть кучи до ее освобождения.Но зачем?

Тогда есть

-(void) kill_timers{
     [timer_porthole invalidate];
     timer_porthole=nil; // 3
}

3 с учетом вашего инициализатора (и, как уже отмечали другие), вы теряете свой таймер здесь;перед этой строкой должно быть [timer_porthole release].


PS:

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

0 голосов
/ 02 марта 2011

Вы пропустили [timer_porthole release]; вызов в вашем kill_timers методе.Если вы вызываете kill_timers до вызова dealloc метода, вы устанавливаете timer_porthole равным nil, но вы не отпускаете его.

...