0

I'm creating a game view controller using an NSTimer to update a progress bar representing the remaining time of the current game round. This game view also displays a button performing some light core data updates. When the remaining time is up, the timer is invalidated and a new view is pushed.

The first time the game view loads in the application life cycle, it works perfectly: no slowdowns when pressing the button and updating core data. But when I'm pushing back the same game view in the application life cycle, button presses make the progress bar choppy and irresponsible.

Is the NSTimer not properly invalidated in the run loop? Should I use CADisplayLink instead, though my view isn't using a lot of resources?

Thanks in advance.

Here is my code:

Timer declaration in .h file :

@property (weak, nonatomic) NSTimer *updatetimer;

Timer creation in viewDidLoad:

self.updatetimer = [NSTimer timerWithTimeInterval:counterStep target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.updatetimer forMode:NSRunLoopCommonModes];

updateProgress function :

  - (void)updateProgress
    {
        //compute current time
        currentTime = currentTime - counterStep;

        //set timer label to int value of current time
        self.timerLabel.text = [[NSString alloc] initWithFormat:@"%d",[[NSNumber numberWithDouble:currentTime] intValue]];

        //update progress bar accordingly
        [self.progressbar setProgress: currentTime / totalTime animated:YES];

        if(currentTime <= 0) 
        {   
            //call the method that invalidates the timer + pushes to the next view
            [self overallTimeEnd];
        }
    }

Timer invalidation:

[self.updatetimer invalidate];
self.updatetimer = nil;
pepollet
  • 1
  • 3
  • Where are you invalidating the timer? Not `dealloc` I hope. Have you confirmed you're hitting that line? – Rob Jul 10 '14 at 11:49
  • The timer is invalidated in a function called by the timer selector when the remaining time is less than 0. I'm not using dealloc on the timer. And yes the invalidation is correctly called each time before leaving the view. – pepollet Jul 10 '14 at 11:54
  • best practice in game programming is that you use one `CADisplayLink` in your app in one main object ... and from there you call functions in your views if needed (like every 10 frames or after certain time has passed) – Bastian Jul 10 '14 at 15:06
  • I read that answer across other threads and I'm ready to apply this solution but I can't understand why it runs smoothly the first time the view loads but not the other times – pepollet Jul 10 '14 at 15:21

2 Answers2

0

There's not enough here to diagnose the problem. Given that you've confirmed that your initial timer is successfully invalidated, the next thing to check is to make sure that the problem is really that updateProgress is not getting called with the desired frequency (rather than whatever is updating the state variables that updateProgress relies upon).

But, let's assume that the problem is truly that updateProgress is not getting called with the desired frequency. If so, then it's probably something that you're running on that main thread that is preventing the timer from being able to run with the desired frequency. It's hard to say without seeing what updateProgress is doing, much less anything else you might have running on the main thread. WWDC 2012 video Building Concurrent User Interfaces on iOS illustrates how to use Instruments to find what might be adversely affecting the concurrent UX.

Finally, it's not clear underlying process that updateProgress is keeping us informed of, but if you can go to an event-driven approach (e.g. if network connection, update UI as didReceiveData is called rather than using a timer). Sometimes you have to use timers, but if you can avoid it, you might be able to improve the efficiency of the app.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • thanks for the video link, i'll watch it later on. As for the content of the updateProgress function, i added it to my question. – pepollet Jul 10 '14 at 15:07
  • @pepollet Nothing terribly extravagant there, so that's unlikely to be the issue. You must have something else occupying the main thread. Hopefully Instruments will help you identify the issue. (FYI, while I agree that if you're doing manual animations, a display link makes more sense, I'm not seeing anything here that would be improved with display link vs timer. Doesn't hurt to try it, but I'd be surprised if it remedies the problem.) – Rob Jul 10 '14 at 19:30
0

So, finally found what was causing my timer not being deallocated over the application life cycle: I wasn't using unwind segues in my storyboard to go back between views but standard push segues. Never used those before and didn't knew about them ... Thanks to all of you for helping.

pepollet
  • 1
  • 3