Prevent NSTimer firing delays in background app - objective-c

I'm working on a macOS app (let's call it the "display app") that displays a clock and other data, which is controlled by another app (the "control app") on the same machine via a TCP connection. I have noticed that when the display app is idle for some time (> 60 sec.) and then schedules an NSTimer (with a .2 second interval), it takes a very long time before the timer fires for the first time (in the range of 6-10 seconds, sometimes longer.) That happens mostly when the display app is not frontmost (because the control app is.) Once the timer fired for the first time, it works as expected (with some small, expected delays in the timer) for some time.
But when the timer is running for a long time (more than 5 minutes), there are similar extreme delays between firing (also 6-10 seconds.) It looks like manually scheduling the timer with
[[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
postpones the problem a bit (using [NSTimer scheduledTimer...] makes that problem appear sooner than when manually adding it to the runloop.)
This causes a lot of trouble because the clock is not updating during that time.
I assume this happens because macOS considers the display app "idle" or "inactive" in some way.
Is there a way to prevent, control, or circumvent this behaviour?

This is App Nap. The display app can do the following to avoid napping:
id activity = [[NSProcessInfo processInfo] beginActivityWithOptions:NSActivityUserInitiatedAllowingIdleSystemSleep reason:#"whatever"];
When it can allow napping again, you should do:
[[NSProcessInfo processInfo] endActivity:activity];

Related

NSTimer slowing down and pausing when minimized [duplicate]

I'm working on a macOS app (let's call it the "display app") that displays a clock and other data, which is controlled by another app (the "control app") on the same machine via a TCP connection. I have noticed that when the display app is idle for some time (> 60 sec.) and then schedules an NSTimer (with a .2 second interval), it takes a very long time before the timer fires for the first time (in the range of 6-10 seconds, sometimes longer.) That happens mostly when the display app is not frontmost (because the control app is.) Once the timer fired for the first time, it works as expected (with some small, expected delays in the timer) for some time.
But when the timer is running for a long time (more than 5 minutes), there are similar extreme delays between firing (also 6-10 seconds.) It looks like manually scheduling the timer with
[[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
postpones the problem a bit (using [NSTimer scheduledTimer...] makes that problem appear sooner than when manually adding it to the runloop.)
This causes a lot of trouble because the clock is not updating during that time.
I assume this happens because macOS considers the display app "idle" or "inactive" in some way.
Is there a way to prevent, control, or circumvent this behaviour?
This is App Nap. The display app can do the following to avoid napping:
id activity = [[NSProcessInfo processInfo] beginActivityWithOptions:NSActivityUserInitiatedAllowingIdleSystemSleep reason:#"whatever"];
When it can allow napping again, you should do:
[[NSProcessInfo processInfo] endActivity:activity];

NSTimer perfomance of device

I want to run a timer that watches my game for some events that occur during the gameplay.
I would include the timer in the did load function. So the Timer is checking permanently if event1 happened or not.
[NSTimer scheduledTimerWithTimeInterval:0.001
target:self
selector:#selector(check_status_event1:)
userInfo:nil
repeats:YES];
I also try to set a pointer to the Timer to invalidate when needed.
- (IBAction)check_status_event1:(id)sender
{
//_stepper_event1_status_check.value (0 = no / 1 = yes)
if (_stepper_event1_status_check.value == 1) {
_stepper_event1_status_check.value = 0; //reset stepper again
[self event1_go:0];
}else{}
}
Question:
Is the interval of 0.001 o.k?
Is there a big influence to the device performance? Because there is permanently running an if statement (or more). Does it maybe also make the iPhone or iPad slower, when the game is sent to background via home button?
Can I use more of this timer at the same time? for example when I run 10 or 20 of them.
Is this the normal way to catch an event that occur during the game? For example a colidation of 2 objects. I also set some code with a timer after 1 sec (step1). after 2 sec (step2). after 3 sec (step3).... when I use such a timer, I can start the next step exactly at this time, when the step before has done. Maybe one step needs more then 1 sec. And I don't want to use animateWithDuration and Delay. And maybe an iPad 2 is slower in processing and reading the whole code as an iPad 4. And I don't want to start with step 2 when step 1 has not finished.
But what is about the performance of the iOS device. Is this a problem?
thank you
Philipp
Is the interval of 0.001 o.k?
Probably not. "The effective resolution of the time interval for a timer is limited to on the order of 50-100 milliseconds". You may set it to 0.001 but you are unlikely to get your timer on time.
Does it maybe also make the iPhone or iPad slower, when the game is sent to background
No it won't slow down the device when in background. iOS stops timers when app is in background.
Can I use more of this timer at the same time? for example when I run 10 or 20 of them.
Yes you can, but you may find a better solution.
I don't want to use animateWithDuration and Delay.
That will make your life easier. iOS goes into extends to make your animation smooth even if the device is under load.
Is this the normal way to catch an event that occur during the game?
No, unless your game doesn't update much, e.g. a turn-base game with no animation. Delivery time of timer event is not guaranteed. It may slip significantly. You would want to update you game state smoothly on every iteration, which is normally a frame. Do you have a frame update loop in your game? That would be the place to check for collisions and count time if needs be.
You are talking about noticing an event. A better design would be to use "push" rather than "pull". When an event happens it notifies all parties interested, not vice versa.

NSTimer missing fire date when clock is manually set forward

I am setting up a timer to run a specific function in the future like this:
pingTimer = [[NSTimer alloc] initWithFireDate:pingAtDate
interval:0
target:self
selector:#selector(ping:)
userInfo:nil
repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:pingTimer forMode:NSDefaultRunLoopMode];
The date is approximately 1 week in the future, so for testing purposes I (and others) have been setting the system clock forward 8 days to ensure that the specified event happens. The problem is that it doesn't happen.
When scheduling a few minutes in the future I have observed that the timer still does go off, but it appears to go off after a specific number of minutes. Say I schedule the timer for a date 5 minutes in the future, then I set the clock forward 1 hour, the timer does actually fire 5 minutes later, but since I set the clock forward 1 hour the time that it fires at no longer aligns with the time that it was scheduled to fire at.
This is not what I would expect to happen, as I am calling "initWithFireDate".
Although all of this seems wrong to me (and may be an interesting observation to others) the question is how do I ensure that the timer fires as soon as it notices that it's fire date is in the past (i.e. How do I ensure that my timer will fire when somebody moves the clock past the scheduled firing date).
There were some good comments left, but no complete answers. I am going to pull all the pertinent details together here.
An NSTimer is not a clock-time mechanism. When you set a "FireDate" you cannot be sure that the timer will actually fire at that date. You are actually telling the timer to run for specific amount of time before firing. That amount of time is the difference between when you add the timer to the run loop, and the date that you scheduled the timer to fire at.
If your system goes to sleep (or your application is suspended), your timer is no longer ticking down. It will resume ticking down when your system wakes up (or your application becomes active), but this means that your timer will now NOT execute at the original "FireDate". Rather it will execute at the "FireDate" + (amount of time your computer was asleep).
Similarly if a user changes the system time, this does not affect the timer in any way. If the timer was scheduled to fire at a date 8 hours in the future, it will continue ticking down 8 hours worth of time before it fires.
In the case where you want a timer to fire at a specific clock time in the distant future, you will need to make sure your application is notified of the following events:
Wake from sleep
System time change
When any of these events occur you will need to invalidate and adjust any existing timers.
/* If the clock time changed or we woke from sleep whe have to reset these long term timers */
- (void) resetTimers: (NSNotification*) notification
{
//Invalidate and Reset long term NSTimers
}
You can observe the following notifications to be notified when those events are going to happen.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(resetTimers:)
name:NSSystemClockDidChangeNotification
object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self
selector:#selector(resetTimers:)
name:NSWorkspaceDidWakeNotification
object:nil];

Show app running time in iPhone

I would like to write a counter that shows how many seconds an app has be running for. (textView) and the counter should be cumulative and starts from where it left off. Im new to iphone sdk.
you are going to have to do these things:
get the time when the app starts in applicationDidFinishLaunching: in your application delegate
check if you have an old time using NSUserDefaults
have a thread that updates the ui (your textView display, may i suggest using a label instead) with the ever increasing time using NSThread or NSTimer - i recommend NSThread. you will also need to perform your updates on your ui thread as updating your ui from a background thread can just get lost in the post.
store the end time in applicationWillTerminate: in your application delegate using NSUserDefaults
lots of googling ahead of you, have fun!

How to display time in seconds in Cocoa efficiently?

I want to display the time (including seconds) in an application in a text field, pretty simple. What I currently do is create a NSTimer that runs every 0.1 seconds that gets the system time and updates the text field. This seems like a terribly inefficient way to accomplish this. Is there a better way?
Are you displaying it to tenth-of-a-second (or finer) resolution?
If so, I see no problem. Usually, polling sucks because what you're checking might not have changed, but it's not like time is going to stop on you. So a tenth-of-a-second timer is fine.
If not, create a timer whose interval is the desired resolution, then get its fire date, round it down to the desired resolution (e.g., a whole second if you update every second), and set that as the new fire date. Then, your timer will only fire coincidentally with the times it's appropriate to update your clock view.
Note that while time always moves, it doesn't always move linearly: it may jump ahead or backward by an hour or half an hour, or any amount if the user (or ntpd) changes the system clock setting. On Mac OS X 10.6 and later, you can observe for the NSSystemClockDidChangeNotification, and re-adjust your timer's fire date when that happens.
How about you use NSTimer that runs every second and then you check the firing time and make sure it is exactly on the start of a second. You can use initWithFireDate:interval:target:selector:userInfo:repeats: or set the next invocation time appropriately using setFireDate: after the timer was created:
NSTimeInterval interval = [[timer fireDate] timeIntervalSinceReferenceDate];
NSTimeInterval nextFire = ceil(interval);
[timer setFireDate: [NSDate dateWithTimeIntervalSinceReferenceDate: nextFire];
Then you'll be sure that the timer fires as close to the needed time as possible and don't worry about the timer lagging behind because it resets itself after each firing using intended firing date and interval you provided, so it will always try to fire at exact second (but will fire whenever the run loop will be able to do that).
I'm not sure what else would be more efficient. As long as the callback for your timer is not doing anything too intensive, you're probably close to optimal.
How about using gettimeofday(2)? It returns much exact time information to use.