1

I get information from a server in my app with a secondsToEnd value and I start a counter after I've received this information.

My project contains a scrollview, so to get around locking my timer due to scrolling around I add the timer to the NSRunLoop in the following way:

[[NSRunLoop currentRunLoop] addTimer:timer
                             forMode:NSRunLoopCommonModes];

I made a NSTimer property called, how original, timer and this is the whole snippet of my startTimer function:

- (void)startTimer
{
    if (_timer || [_timer isValid]) [_timer invalidate], _timer = nil, [_timer release];

    NSTimer * timer = [[NSTimer alloc] initWithFireDate:[NSDate date]
                                               interval:1.0
                                                 target:self
                                               selector:@selector(timer:)
                                               userInfo:nil
                                                repeats:YES];

    [[NSRunLoop currentRunLoop] addTimer:timer
                                 forMode:NSRunLoopCommonModes];

    [self setTimer:timer];
    [timer release];
}

The reason for the check to invalidate in the start method is because after the secondsToEnd value hits 0, I receive a new one and I call startTimer again.

And in my dealloc method I have this:

if (_timer || [_timer isValid]) [_timer invalidate], _timer = nil, [_timer release];

But it doesn't get invalidated? What am I doing wrong?

Rick van der Linde
  • 2,581
  • 1
  • 20
  • 22

2 Answers2

3

What order do these comma separated statements execute in? What happens if _timer is nil when you call invalidate or when you call release?

if (_timer || [_timer isValid]) [_timer invalidate], _timer = nil, [_timer release];

Try this instead, there is no need to check whether _timer is already nil. If it was nil then the method call does nothing.

if ([_timer isValid]) {
    [_timer invalidate];
}
[_timer release];

In the dealloc method there is no need to set _timer = nil, its storage is gone after this method ends.

progrmr
  • 75,956
  • 16
  • 112
  • 147
  • I tried it, but with no luck. It does work in any method other then dealloc though, somehow it feels like there is some second counter sneaked in somewhere. But I changed my code to add the timer to a mutable array, and the count was always 1. – Rick van der Linde Sep 20 '11 at 12:59
  • A mutable array will retain the timer and it will never get deallocated unless you release the array. – progrmr Sep 20 '11 at 14:10
1

You should first call [timer release] and then timer = nil. Same in dealloc method. The dealloc method might no get called immediately if outer objects are in autrelease pools. In these cases it is called when the system decides to drop your object finally. So it may take some time (if you set a breakpoint).

BTW I would suggest to avoid comma syntax and use blocks within curly braces instead. It's far more easier to read.

Kay
  • 12,918
  • 4
  • 55
  • 77
  • I think I used blocks once, with some animation code. How would you use that in this case? The switch of release and nil didn't fix it btw :( – Rick van der Linde Sep 20 '11 at 12:57
  • I think I misread your comment about blocks, did you mean it like this: NSTimer * timer = { [[NSTimer alloc] initWithFireDate:[NSDate date] interval:1.0 target:self selector:@selector(timer:) userInfo:nil repeats:YES] }; – Rick van der Linde Sep 20 '11 at 13:33
  • Yes, no blocks in the meaning of functional programming but just structuring the code - sorry it was a bit ambiguous :-) Like progrmr said, I am not sure if it's executed from left to right. But there seems to be another problem. What is the result, is timer method called twice as often as it should? Can you change the implementation to reuse the timer instead of deleting and recreating it? – Kay Sep 20 '11 at 15:18
  • Look at http://stackoverflow.com/questions/1449035/how-do-i-use-nstimer Maybe this addTimer call increments the release count. – Kay Sep 20 '11 at 15:23
  • The runloop retains the timer. It won't release it until you invalidate it, so doing the invalidate in the dealloc is pointless (dealloc doesn't get called until the last release). – progrmr Sep 20 '11 at 17:01