NSTimers导致泄漏

时间:2011-03-02 20:01:18

标签: iphone xcode ipad memory-leaks nstimer

我已经阅读了很多关于NSTimers的内容,但我必须对它们做一些非常错误的事情,因为它实际上是Leaks仪器中出现的所有泄漏。 “责任框架”栏目显示 - [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 个答案:

答案 0 :(得分:4)

请勿在您的NSTimer上调用retain!

我知道这听起来有点反直觉但是当你创建实例时,它会自动注册到当前(probaby main)线程运行循环(NSRunLoop)。这就是苹果公司在这个问题上所说的话......

  

计时器与run一起工作   循环。要有效地使用计时器,你   应该知道如何运行循环   操作 - 参见NSRunLoop和线程   编程指南。 特别注意   运行循环保留其计时器,所以   你有可以释放计时器   将其添加到运行循环中。

     

一旦在运行循环上安排,就可以了   计时器以指定的间隔触发   直到它失效。一个   非重复计时器使自身无效   火灾发生后立即发生。然而,   对于重复计时器,你必须   自己使计时器对象无效   通过调用其invalidate方法。   调用此方法请求   从当前删除计时器   运行循环;结果,你应该   总是从中调用invalidate方法   与计时器相同的线程   安装。 计时器无效   立即禁用它,以便它没有   更长时间会影响运行循环。运行   然后循环删除并释放   计时器,就在之前   invalidate方法返回或在某些方面返回   以后点。一旦失效,计时器   对象无法重复使用。

所以你的实例化变成......

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

现在您不再持有对实例的引用,您不希望在dealloc方法中调用release。

答案 1 :(得分:3)

我见过你已经接受了答案,但我想在这里纠正两件事:

  1. 不需要来保留计划的计时器,但它不会造成任何伤害(只要您在不再需要时释放它)。计时器/目标关系的“有问题”部分是......
  2. 计时器保留其目标。而且你决定将目标设定为self 这意味着 - 保留或不保留 - 只要计时器有效,计时器就会使你的对象保持活着状态。
  3. 考虑到这一点,让我们从下到上重新审视您的代码:

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

    1 毫无意义:
    如果timer_porthole仍然是一个有效的计时器(即在runloop上安排)它将保留你的对象,所以这个方法不会被调用...

    2 这里没有意义:
    这是dealloc!当[super dealloc]返回时,将释放实例在堆上占用的内存。当然,你可以在释放之前将你的部分内容弄清楚。但为什么要这么麻烦?

    然后有

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

    3 鉴于您的初始化程序(正如其他人指出的那样),您在此处泄露计时器;在此行之前应该有一个[timer_porthole release]


    PS:

    如果你认为一切都结束了,你会发现保留计时器(至少是暂时的)会创建一个保留周期。在这种特殊情况下恰好是一个非问题,一旦计时器失效就解决了......

答案 2 :(得分:0)

您在[timer_porthole release];方法中错过了kill_timers来电。如果您在调用kill_timers方法之前致电dealloc,则将timer_porthole设置为nil,但您没有将其释放。