NSTimers causing leaks

二次信任 提交于 2019-12-06 21:17:28

Don't call retain on your NSTimer!

I know it sounds counter-intuitive but when you create the instance it's automatically registered with the current (probaby main) threads run loop (NSRunLoop). Here's what Apple have to say on the subject...

Timers work in conjunction with run loops. To use a timer effectively, you should be aware of how run loops operate—see NSRunLoop and Threading Programming Guide. Note in particular that run loops retain their timers, so you can release a timer after you have added it to a run loop.

Once scheduled on a run loop, the timer fires at the specified interval until it is invalidated. A non-repeating timer invalidates itself immediately after it fires. However, for a repeating timer, you must invalidate the timer object yourself by calling its invalidate method. Calling this method requests the removal of the timer from the current run loop; as a result, you should always call the invalidate method from the same thread on which the timer was installed. Invalidating the timer immediately disables it so that it no longer affects the run loop. The run loop then removes and releases the timer, either just before the invalidate method returns or at some later point. Once invalidated, timer objects cannot be reused.

So your instantiation becomes...

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

And now that you're no longer holding the reference to the instance you wont want the release call in your dealloc method.

I've seen you already accepted an answer but there are two things here that I wanted to rectify:

  1. It's not needed to retain a scheduled timer but it doesn't do any harm (as long as you release it when it's no longer needed). The "problematic" part of a timer/target relationship is that...
  2. a timer retains its target. And you've decided to set that target to self.
    That means — retained or not — the timer will keep your object alive, as long as the timer is valid.

With that in mind, let's revisit your code from bottom to top:

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

    [super dealloc];
}

1 is pointless:
If timer_porthole was still a valid timer (i.e. scheduled on a runloop) it would retain your object, so this method wouldn't be called in the first place...

2 no point here, either:
This is dealloc! When [super dealloc] returns, the memory that your instance occupied on the heap will be freed. Sure you can nil out your part of the heap before it gets freed. But why bother?

Then there is

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

3 given your initializer (and as others have pointed out) you are leaking your timer here; there should be a [timer_porthole release] before this line.


PS:

If you think it all over, you'll see that retaining the timer (at least temporarily) creates a retain-cycle. In this particular case that happens to be a non-issue which is resolved as soon as the timer is invalidated...

You missed [timer_porthole release]; call in your kill_timers method. If you call kill_timers before dealloc method is called, you set timer_porthole to nil, but you did not release it.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!