Xcode / Objective-C: Why is NSTimer sometimes slow/choppy? - iphone

I am developing an iPhone game and I have an NSTimer that animates all of the objects on the screen:
EverythingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0 target:self selector:#selector(moveEverything) userInfo:nil repeats:YES];
Most of the time it runs very smoothly, but sometimes I see things move slower or choppy. I have pause and resume functions that stop and start the timers respectively. When I pause then unpause, it seems to fix the choppiness.
Any ideas to why this is happening? or How can i fix it?

Any ideas to why this is happening?
The short answer is that in your case, you are performing actions (animations) based on an unsynchronized push model, and the mechanism you are using is not suited for the task you are performing.
Additional notes:
NSTimer has a low resolution.
Your work is performed on the main run loop. The timer may not fire when you expect it to because much time may be spent while work on that thread is underway, which blocks your timer from firing. Other system activities or even threads in your process can make this variation even greater.
Unsynchronized updates can result in a lot of unnecessary work, or choppiness because the updates you perform in your callback(s) are posted at some time after they occur. This adds even more variation to the timing accuracy of your updates. It's easy to end up dropping frames or perform significant amounts of unnecessary work when the updates are not synchronized. This cost may not be evident until after your drawing is optimized.
Out of the NSTimer docs (emphasis mine):
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 during a long callout or while the run loop is in a mode that is not monitoring the timer, 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.
The best way to remedy the issue, if you are prepared to also optimize your drawing -- is to use CADisplayLink, as others have mentioned. The CADisplayLink is a special 'timer' on iOS which performs your callback message at a (capable) division of the screen refresh rate. This allows you to synchronize your animation updates with the screen updates. This callback is performed on the main thread. Note: This facility is not so convenient on OS X, where multiple displays may exist (CVDisplayLink).
So, you can start by creating a display link and in its callback, perform work which would deal with your animations and drawing related tasks (e.g. perform any necessary -setNeedsDisplayInRect: updates). Make sure your work is very quick, and that your rendering is also quick -- then you should be able achieve a high frame rate. Avoid slow operations in this callback (e.g. file io and network requests).
One final note: I tend to cluster my timer sync callbacks into one Meta-Callback, rather than installing many timers on run loops (e.g. running at various frequencies). If your implementations can quickly determine which updates to do at the moment, then this could significantly reduce the number of timers you install (to exactly one).

NSTimers are not guaranteed to run at exactly the rate you asked for. If you're developing a game, you should investigate CADisplayLink, which implements roughly the same interface as NSTimer but is fired at every screen redraw.
You set up a display link just like your timer, only you don't have to specify a refresh period.
CADisplayLink* displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(moveEverything)];
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
Above all you should measure the amount of time that has passed since the last iteration. Don't rely on it being exactly 1/30th or 1/60th of a second. If you have code like this
thing.origin.x += thing.velocity * 1/30.0;
change it to this
NSTimeInterval timeSince = [displayLink duration];
thing.origin.x += thing.velocity * timeSince;
If you need a high-precision timer for a reason other than making a game, see Technical Note TN2169.

As Hot Licks suggested, 30 Hz is reasonably fast, so depending on what you're doing inside moveEverything, you might simply be overwhelming your phone's processor. You don't show moveEverything, so we can't really comment on what's inside ... but, you may need to work at making that method more efficient, or slowing down the rate at which your timer fires.
Also, it's possible that your NSTimer is simply not firing at times, because the timer has not been added for all the right run loop modes.
For example, when a scroll view scrolls (a performance intensive operation), the UI is in tracking mode. By default, the way you've created your timer, the timer will not fire when in tracking mode. You can change this by using something like this:
EverythingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
target:self
selector:#selector(moveEverything)
userInfo:nil
repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:EverythingTimer forMode:NSRunLoopCommonModes];

Related

Using a timer vs update to run game SpriteKit

I am curious what to use for my game. A timer:
let goodFPS = SKAction.wait(forDuration: 0.01666)
let mainGameRunner = SKAction.run {
//my code is here
}
let toRepeat = SKAction.repeatForever(SKAction.sequence([goodFPS,mainGameRunner]))
inGameHighScore.run(toRepeat,withKey:"mainGame")
or the update function:
override func update(_ currentTime: TimeInterval){
//my code is here
}
Which provides faster more consistent updates?
note: my frame rate is in the range of 45 to 58
First I think you are taking the FPS problem the wrong way around. You cannot "force" a faster frame rate than the device can give you. If you are basing the movements in your game on the assumption that every frame will be consistent, you are doing it wrong. It's actually how they did in the early days because CPUs were so slow and the difference from one generation to the new one wasn't too bad at first. But running an old DOS game on younger hardware will be tricky because the framerate is so high that the whole game mechanic becomes unstable or simply too fast to be playable.
The concept here is to think "over time" and to scale down any action in relation with the time elapsed between two frames.
The update() method gives you that opportunity by providing the current system clock state every frame. By keeping track of the time on the last frame, you can calculate the time difference with the current frame and use that difference to scale down what you are doing.
Using a timer to get the update on a consistent frame rate is not recommended nor practical. You may be calling the update closure at a given time interval, but the code inside that closure is taking time to execute on its own, and depending on your game logic, it might even have different execution times. So maybe the pause timing is consistent, but the code running before and after that pause might not be consistent. Then what happens if you run your game on a slower CPU? The code speed will change even more, making your timing inaccurate.
Another point against using an SKAction for your game loop is simply what they are. An action is an object in memory, meany to be reused by multiple objects. If you are making a "jump" action, for example, it is recommended to store that action somewhere and to reuse the same object every time you need something that "jumps", no matter what node it is. Your game loop is meant to be executed every frame, but not by different objects. Actions are also disposable. Meaning that you can kill an action even while it's running. If you put your game loop in an action, it will probably be run by the SKScene. If you use another action on your scene it becomes a puzzle right away because there are only two ways of removing an action besides letting it come to term: removing all actions or creating the action with an identifier key and use that key to remove any action with that key. If you don't see it already, it then forces you to put identifiers on every action that will be run by the scene and remove them one by one. And them again it leave a door open for a mistake that will get rid of your game loop because, keep it in mind, actions are DISPOSABLE! Then there is also no guarantee that your action will get executed first every single frame.
Why use the update() method? Simply because it is built IN your scene. No matter what, every frame, update() gets called first. THEN, the actions get executed. You cannot flush the update() method accidentally like you can with an action. You don't have to be careful about strong/weak relationships causing memory leaks because you are referring to objects from inside a closure like you do with an action.
Suggested reads:
SKAction API reference
SKScene API reference : read about the frame processing in SpriteKit. It will help you understand how they put everything together at every frame.
I hope it makes things clearer.
I'm pretty sure that SKAction's timing facilities are based on the same game loop that is calling update.
The advantage of SKAction is that it's fire and forget for you, while using update would get awkward with setting and checking a bunch of timer variables.
I don't have a ton of experience with SpriteKit but I do have some experience making games and animations in iOS.
There is a class called CADisplayLink that fires a call to a delegate every time the screen is refreshed, this is a great way to update the screen, either in a game or in an animation, because you can know it will be called every frame and no more.
I'm not sure if SpriteKit uses that class to fire the update method, but I'm sure it uses something similar. This is usually called the run loop.
SKActions run on top of this run loop.
By creating your own run loop using a wait action, not only you're not gaining any benefits, you could be introducing inconsistencies in the way your logic is run.
Imagine that you have your wait action set to 0.01 seconds (I rounded it down for simplicity). If the screen is refreshing faster than you expect, and the action is updated every 0.009 seconds, the first time it's checked, it won't fire because there's a remaining 0.001 second on the wait command, so another 0.009 seconds will pass, and your code will be executed after 0.018 seconds, instead of your desired 0.01. Not only that, but two frames will have passed between the execution.
If you use the update function, you can know that your logic will be executed once every screen refresh, and no more.

can a timer trigger during another timer's callback?

I have two timers running simultaneously. The first timer triggers every 1 second and takes 0.2 seconds to run. The second timer triggers every 20 minutes and takes 5 minutes to run. I would like to have the first timer continue triggering during the 5 minutes it takes the second timer execute its callback. In practice, during the second timer's callback the first timer does not trigger. Is it possible to configure the timers to execute the way I want?
There is a workaround, depending on how your timer callbacks' work is structured. If the long timer callback is running a long loop or sequence of calls to different functions, you can insert drawnow() or pause(0.01) calls to make it yield to Matlab's event dispatch queue, which will handle pending handle graphics and timer events, including your other Timer's trigger.
It's sort of like old-school cooperative multitasking where each thread had to explicitly yield control to other threads, instead of being pre-empted by the system's scheduler. Matlab is single-threaded with respect to M-code execution. When a Matlab function is running, events that get raised are put on an event queue and wait until the function finishes and returns to the command prompt, or drawnow(), pause(), uiwait() or a similar function is called. This is how you keep a Matlab GUI responsive, and is documented under their Handle Graphics stuff. But Matlab timer objects use the same event queue for their callbacks. (At least as of a couple versions ago; this is only semi-documented and might change.) So you can manage their liveness with the same functions. You may also need to tweak BusyMode on your timers.
This is kind of a hack but it should get you basic functionality as long as you don't need precise timing, and don't need the callbacks' code to actually run in parallel. (Whichever timer callback has yielded will wait for the other one to finish before proceeding with its own work.)
If the long callback is really blocked on a long operation that you can't stick drawnow calls in to, you're out of luck with basic Matlab and will need to use one of the workarounds the commenters suggest.

Anything wrong with leaving timer on and using a flag to control execution on/off?

I'm polling a router from an iPhone. The timer only fires every 60 seconds. I'm starting the timer in ViewDidLoad and leaving it on.
There's a flag pollingON initially set FALSE.
When the timer fires, it calls myTimerFiredMethod, which then checks,
if (self.pollingON) {
self.pollingON = FALSE ;
// run the polling code
self.pollingON = TRUE ;
}
Aside from wasting a nanosecond or two of CPU time, anything wrong with this practice?
A timer that is running although not in use, is a waste of battery life. It's also a strange design pattern, running a timer without a cause.
Also if you turn on Polling, at worst you will have to wait almost 60 seconds before an update.
I would not recommend implementing it that way. Instead, invalidate the timer when polling is turned off and create a new timer when polling is turned on.

Is it possible to use a timer to kill rogue method in background thread?

I have a piece of code that calls a method (i.e. processRegex) which is from a 3rd party library, regexKitLite, that runs a regex match on a bunch of data. I am running this method in a bg thread using performSelectorInBackground:. I have found that in some cases if the data is invalid or corrupted then the processRegex method locks up the thread and gets caught in a recursive loop which could take forever to come back which i obv would like to avoid.
I was thinking of using a timer that basically waits x amount of time and if it hasnt been invalidated before the specified time passes it then kills the bg thread that is running the method.
My problem however is:
I cant launch the timer in the same thread as the regex method because that thread could lock up while trying to process the data and wouldnt call my timer as i tried below.
// setup timer to restrict the amount of time a regex can take before killing it
myTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(resultProcessingTimedOut:) userInfo:nil repeats:NO];
resultsArray = [NSMutableArray arrayWithArray:[html arrayOfCaptureComponentsMatchedByRegex:]; // <-- could lock up thread!!
[myTimer invalidate];
Secondly, according to the apple docs you have to invalidate the timer in the same thread that started it and i cant launch the timer in its own background thread because im not sure how i would then invalidate it?
Lastly, how would i even kill the thread that is running the regex as you are not supposed to terminate threads.
Any ideas on how to deal with this situation???
thx
In short, no, you cannot kill another thread arbitrarily. There is no way to know what state it'll be in when killed and, thus, the memory leaks and/or corruption are bound to happen.
You can solve this in one of several ways (including combining):
modify the regexkit to have some kind of "stop" flag that can be set after the timer fires in another thread. Periodically check it and clean up / stop when needed.
validate or limit the input regular expression and input data to ensure that the slow/long situation doesn't happen
break up the input data such that you can batch process it.

How to handle class methods being called again before they are finished?

What is the best way to handle this situation on an iPhone device: My program ramps the pitch of a sound between two values. A button pressed calls a method that has a while loop that does the ramping in small increments. It will take some time to finish. In the meantime the user has pressed another button calling the same method. Now I want the loop in the first call to stop and the second to start from the current state. Here is the something like what the method should look like:
-(void)changePitchSample: (float) newPitch{
float oldPitch=channel.pitch;
if (oldPitch>newPitch) {
while (channel.pitch>newPitch) {
channel.pitch = channel.pitch-0.001;
}
}
else if (oldPitch<newPitch) {
while (channel.pitch<newPitch) {
channel.pitch = channel.pitch+0.001;
}
}
}
Now how to best handle the situation where the method is called again? Do I need some kind of mulitthreading? I do not need two processes going at the same time, so it seems there must be some easier solution that I cannot find (being new to this language).
Any help greatly appreciated!
You cannot do this like that. While your loop is running no events will be processed. So if the user pushes the button again nothing will happen before your loop is finished. Also like this you can’t control the speed of your ramp. I’d suggest using a NSTimer. In your changePitchSample: method you store the new pitch somewhere (don’t overwrite the old one) and start a timer that fires once. When the timer fires you increment your pitch and if it is less than the new pitch you restart the timer.
Have a look at NSOperation and the Concurrency Programming Guide. You can first start you operation the increase the pitch and also store the operation object. On the second call you can call [operation cancel] to stop the last operation. Start a second operation to i.e. decrease the pitch and also store the new object.
Btw: What you are doing right now is very bad since you "block the main thread". Calculations that take some time should not be directly executed. You should probably also have a look at NSTimer to make your code independent of the processor speed.
Don't use a while loop; it blocks everything else. Use a timer and a state machine. The timer can call the state machine at the rate at which you want things to change. The state machine can look at the last ramp value and the time of the last button hit (or even an array of UI event times) and decide whether and how much to ramp the volume during the next time step (logic is often just a pile of if and select/case statements if the control algorithm isn't amenable to a nice table). Then the state machine can call the object or routine that handles the actual sound level.