Schedule timer with NSTimer makes the task run faster than expected - iphone

I schedule a timer with NSTimer's function in viewWillAppear as follows:
minutesTimer = nil;
minutesTimer = [NSTimer scheduledTimerWithTimeInterval:60 target:self selector:#selector(updateScrollViewItems) userInfo:NULL repeats:YES];
With this function call, I expect it to call the selector updateScrollViewItems every minute, but it does not, it update items faster than expected (around some seconds).
What is the cause of this unexpected behavior?

It might not be the direct cause of the issue you're seeing, but I can already spot one major error.
The fact that this timer is set up each time on viewWillAppear means that whenever your view appears, you're creating a new timer (and leaking the old one) which will fire 60 seconds after creation.
If your view disappears and reappears multiple times, you're going to have multiple timers all firing the same method at completely random intervals.
You need to manage the timer properly. If you want it to start when the view is first created and keep ticking/firing even when the view is not shown, then you need to create it during init, or viewDidLoad, and then be sure to stop it when you dealloc or viewDidUnload.
If you want your timer to only tick/fire when the view is the current view, then you need to ensure you're managing stopping and starting the timer appropriately on viewDidAppear and viewWillDisappear.
Also, as Williham Totland said in his answer, NSTimer shouldn't be relied upon for exact timing. This is also stated in the documentation:
A timer is not a real-time mechanism; it fires only when one of the run loop modes to which the timer has been added is running and able to check if the timer’s firing time has passed. Because of the various input sources a typical run loop manages, the effective resolution of the time interval for a timer is limited to on the order of 50-100 milliseconds. If a timer’s firing time occurs while the run loop is in a mode that is not monitoring the timer or during a long callout, the timer does not fire until the next time the run loop checks the timer. Therefore, the actual time at which the timer fires potentially can be a significant period of time after the scheduled firing time.
In this case, with a time span of 60 seconds, it shouldn't be a problem that the timer is not exact, I think the issues you're seeing are because the timer isn't being managed properly.

The iPhone is a lot of things; and a neat device, however; just like any other computer, a precision timepiece it is not. However, it seems as if the method in question takes an NSTimeInterval, which is a double. You might want to change that to 60.0.

If you want your timer to only
tick/fire when the view is the current
view, then you need to ensure you're
managing stopping and starting the
timer appropriately on viewDidAppear
and viewWillDisappear.
Thanks for this info, even though I'm not the OP of this thread I was looking for an answer for a similar issue. I was scratching my head for few days, searched all over the internet but I couldn't find the answer. Finally I found this thread. This is a valuable info for me.

Related

iOS: multiple NSTimer instances using the same view - do I need to use multithreading?

I'm working on a multiview app. One of the views is a table view. Each cell has a stopwatch. I'm planning to use NSTimer for the stopwatches. Do I need to implement multithreading for the timers to work properly even when the user switches the view and then comes back later?
I did my research but most of the tutorials cover one NSTimer in a single view. I want to make sure the user can do other things while the timers are running, like use the interface, navigation, etc. In another post Placing an NSTimer in a separate thread someone said you need a different runloop for the timer. Would I need one runloop for each timer in my case? Is it advisable? Any performance drawbacks?
Thanks a lot!
One run loop should be just fine. Your interface will still be responsive.
Keep in mind that timers are never guaranteed to be accurate. They are affected by how much other stuff is on the same loop. Its ok to use the timer to update the display but not to actually measure time. Set an NSDate when you start a stop watch then compare the current date with that start date each time your display timer updates the display.
Since you should only use the NSTimer to update the display, could you just use one generic display update timer that updates all running stopwatches, instead of having one for each stopwatch?

How to keep NSTimer alive?

I have 2 viewControllers in my app, from first view when i navigate to next one there is a button named startTimer with a timer action as selector method on Click of startTimer the timer starts up in HH:mm:ss format, i am not invalidating timer,but When i go back to 1st viewController and again if i come to again 2nd viewController and if i press startTimer button the timer again starts from 0, but i want it to be retained the previous value,how can i achieve this? i know that since i'm loading again the viewController the nib will be loaded freshly to memory but how can i retain the timer label and timer value?
Any help is appreciated in advance.thank you.
You've broken MVC (Model-View-Controller) by putting your data into your view controller. Moreover, you're asking a mechanism with no solid promises about time (NSTimer) to keep track of time for you. NSTimer does not fire exactly at the interval you request. It can fire at any arbitrary point after that interval. Using NSTimer as a stopwatch will almost always lose time (sometimes quite a lot of time, particularly if there's a scrollview around). (That last bit is overstated. A repeating timer schedules itself correctly so won't usually lose time. You'll just lose time if a repeat is completely skipped, which can happen during long scrolls or other things that can keep timers for firing for a full second.)
Create a new model object to hold the stopwatch information. Let's call it Stopwatch. Assuming you need it to be startable and stoppable, it needs an NSTimeInterval accumulatedTime property and an NSDate lastStarted property (you could also make lastStarted an NSTimeInterval if you like). So to start the stopwatch, you set lastStarted to "now." To stop the stopwatch, you clear lastStarted and move the current accumulated time to accumulatedTime. To find out the current time, you add accumulatedTime to now - lastStarted.
OK, now that you have that, what can you do with it? You can pass it to your view controllers and they can ask "what's the current stopwatch value?" They can start and stop it as they like.
Now your view controller would like to update its display every second, so you have a timer that does that. Every second it asks the stopwatch, "what's the current time" and it displays it. But it does not set the time. It just asks.
BTW, you can also use KVO on Stopwatch, but it's a little trickier, since Stopwatch needs to run its own timer to send out the change notifications. I generally find this more trouble than its worth.
Don't do like that, this way every time you load the view containing the NSTimer, a new NSTimer object is created and the old one is still in the memory since you're not invalidating it.
The best way is that you must put NSTimer in the Application Delegate and then start it only when you first time load that View Controller.
For achieving this, you must put a flag to check that the View is loaded first time or not.
If the NSTimer is one of your instance variable, I guess you could do a check to see if it is allocated or not.
//NSTimer *timer; declare this in your interface
if (timer==nil)
{
// allocate timer
}
//Do nothing if it is allocated all ready
What I would do is to mantain the second view controller as an ivar of your first view controller. This way, you can instantiate it just once and your NSTimer will remain in memory.
However, if you want to maintain your style, you should save the current time in any kind of preferences (look at [NSUserDefaults standardUserDefaults] or create your own singleton class). Then in view did load method from your second view controller, load that value and add it as an offset for your timer.
Hope to help!

Increased Speed while using NSTimer

I have created an animated game app, In which I am using NSTimer to move an image after a particular interval of time.
timer = [NSTimer scheduledTimerWithTImerInterval:0.2 target:self selector:#selector(MoveVirus) userinfo:nil repeats:YES];
this timer calls up the function MoveVirus, MoverVirus moves virus (an Image on screen).
It works fine in the beginning but the speed of motion automatically increases.
The increased speed of motion destroys every further logic.
I don't know what is the problem with it?
Please help to solve this problem.
NSTimer isn't necessarily meant for this sort of use...From the docs on NSTimer:
A timer is not a real-time mechanism; it fires only when one of the
run loop modes to which the timer has been added is running and able
to check if the timer’s firing time has passed. Because of the various
input sources a typical run loop manages, the effective resolution of
the time interval for a timer is limited to on the order of 50-100
milliseconds. If a timer’s firing time occurs while the run loop is in
a mode that is not monitoring the timer or during a long callout, the
timer does not fire until the next time the run loop checks the timer.
Therefore, the actual time at which the timer fires potentially can be
a significant period of time after the scheduled firing time.
A better approach, if you are moving UIImages that are contained in a UIImageView would be to use the class animation methods on UIView. You can still get the same result of moving the image after a particular time if you use the animation method that contains a delay. The method is:
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
Using this method, you can specify the time of the animation (how long it takes to animate the move of your UIImages), how long to wait before starting this animation, a set on animation options, a block of animation code, and a block that executes when the animation is complete.

How to fire 10 messages per second?

Problem: I need to fire values to an object 10 times per second. I must be able to start firing and stop firing.
I've discovered that the delegate of an UIScrollView gets notified in about the same time intervals, no matter how fast the scrolling is. You can easily see that if you NSLog the deltas from the offset changes. There must be a way to start firing a message 10 times per second until something says "stop". But how?
I would look into the NSTimer class. That should allow you to set up a timer with an arbitrary time span (and stop it when desired).
I'm not much of a Cocoa developer, but I'm pretty sure you want NSTimer.
Look into [NSTimer timerWithTimeInterval:invocation:repeats:], [NSTimer invalidate], and NSRunLoop.

Timer and animation events trumping TouchesEnded events

I've implemented a tap-and-hold handler using an NSTimer that I first set in the TouchesBegan overload.
However, what I actually want is for an action to be continuously performed in quick-fire succession while the touch is being held. So, on timer expiry I call a handler to do the work, which then sets another timer and the cycle continues until the TouchesEnded comes in and cancels it, or another terminating condition is met.
This works fine, until my handler code triggers an animation to go off at the same time.
Now we have animation events and timer events going off, and in all that we need to handle TouchesEnded as well.
What I am finding is that, if the animation is triggered, and I set my timer to less than 0.025 seconds, my TouchesEnded event doesn't come through until the timer cycle stops (the other terminating condition). Setting a slower timer, or not triggering the animation, make it work (TouchedEnded comes in straight away), but are not what I want.
Obviously this is all on the device (release build - no NSLogs) - in the sim it all works fine
Is there any way of setting the relative priorty of these events - or is it likely I'm missing something else obvious here?
[Update]
I've worked around this in this instance by doing the continuous part without visual feedback until it's done (which from this users perspective is instant). I think this is ok for now. I'd still like to hear any more thoughts on this (Jeffrey's idea was good), but I'm not waiting on tenterhooks now.
Try writing your own Timer-type class by spawning off onto a thread. Example:
BOOL continue = YES; //outside of your #implementation
-(void)doLoop
{
while(continue){
[NSThread sleepForTimeInterval:.025];
[self performSelectorOnMainThread:#selector(whateverTheFunctionIs) waitUntilDone:YES];
}
}
and this would be started by [NSThread detatchNewThreadSelector:#selector(doLoop) toTarget:self withObject:nil]. This is not exactly threadsafe, but you can choose to wrap the boolean into a NSNumber and then do #synchronize on it if you so choose. Alternatively, after I wrote that little snippet I realized it would be better to do a check against the current NSTime instead of sleepForTimeInterval: but you get the point. :)