is the NSTimer monotonic timer

Hi, I have a question, is the NSTimer monotonic timer or wall clock?

Replies

[I have removed the body of this post because I accidentally posted it twice.]

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

That’s a surprisingly complex question. There’s actually three aspects to this:

  • The

    NSTimer
    API works in terms of wall clocks (for example, the
    fireDate
    property is an
    NSDate
    ).
  • Last I checked its implementation was based on a monotonic clock (be aware I’m using monotonic in the dictionary sense, that is, it never decreases).

  • However, that clock stops during system sleep.

For short-running timers this doesn’t matter. For long-running timers I recommend three things:

  • Rebuild your timers after a clock change (

    NSSystemClockDidChangeNotification
    ).
  • Avoid running timers over system sleep. Instead cancel them on sleep and re-set them on wake.

  • If you want a monotonic timer that runs across system sleep, check out the various clock APIs we recently added to

    <time.h>
    . The man page has a clear description of the semantics of each.

IMPORTANT With regards the second point above, if you’re working on an iOS-based platform there’s no easy way to detect system sleep. Rather, you should cancel the timer when your app becomes eligible for suspension and resume it when that’s no longer the case. Typically this means when your app is moved to the background and then back to the foreground.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

thank you for you reply.


when i see dispatch_source_timer, i found this method:

void dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway);


as the document say :

"The

start
time parameter also determines which clock is used for the timer. If the start time is
DISPATCH_TIME_NOW
or is created with
dispatch_time
, the timer is based on
mach_absolute_time
. Otherwise, if the start time of the timer is created with
dispatch_walltime
, the timer is based on
gettimeofday
(3)."


if i use dispatch_walltime method get

dispatch_time_t,
the dispatch_source_timer can run in background ?

if i use

dispatch_walltime
method get
dispatch_time_t
, the
dispatch_source_timer
can run in background ?

That depends on what you mean by “run in background”. If you’re asking “Will the timer resume a suspended app in the background?”, the answer is “No, there’s no API on the system that will resume a suspended app at a specific time.”

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

thank you for you reply.



- (void)startTimer {
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1
                                                  target:self
                                                selector:@selector(timerFired:)
                                                userInfo:nil
                                                 repeats:YES];
}

static NSInteger count = 0;
- (void)timerFired:(NSTimer *)timer {
    count++;
    NSLog(@"count == %@",@(count));
}


when i run these code , i found when app enter background, the timer can not log , but when i active app to foreground, the timer can work.

it is no problem.


but, when i run follow code :


- (void)startTimer {
    self.timer = [NSTimer scheduledTimerWithTimeInterval:60
                                                  target:self
                                                selector:@selector(timerFired:)
                                                userInfo:nil
                                                 repeats:NO];
}

static NSInteger count = 0;
- (void)timerFired:(NSTimer *)timer {
    count++;
    NSLog(@"count == %@",@(count));
}


when i start the timer, after 45s make the app enter background, then after 10s active the app enter foreground , after 5s the timer log the

"count == 1"


i have a question is:

the timer in foreground is 50s , in background is 10s, the timer can not work in background, in fact in total 60s timer only work 50s,

why the timer make a log .

why the timer make a log.

When you put the app in the background for 10 seconds, did the device sleep? Earlier I wrote:

However, that clock stops during system sleep.

There’s a difference between system sleep and app suspend. When you put the app into the background, it is typically suspended shortly thereafter (unless there’s something specifically prevent that suspension, like a

UIApplication
background task). However, that does not guarantee that the system sleeps as a whole. There’s no easy way to guarantee that. For example, locking the screen does not guarantee system sleep, because some other app (or system service) could hold an assertion that prevents it from being suspended, and the system can’t sleep while such an assert is being held.

Regardless, I stand by my advice from 23 Jul: Rather than try to reverse engineer all the odd behaviours you can get out of

NSTimer
, my recommendation is that you try to avoid its edge cases entirely.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

thank for you reply

as you said, i put the app in the background, but the device not sleep.


why repeats is NO timer can run in app background when device not sleep?

if i set repeats is YES , the timer can not run in app background when device not sleep?

I think the word "run" is confusing you.


A timer can not fire when the app is in the background. However, so long as the device has not slept, the timer still keeps correct time. The time spent in the background is no different than time spent in the foreground.


The difference in behavior between your two code snippets is not "repeat:NO" vs. "repeat:YES". It's simply whether the timer would have fired except the app was in the background. If the fire time passes when the app is in the background, the firing is delayed until it is in the foreground. But, if the fire time doesn't occur while your app is in the background, then the time it spent in the background doesn't affect the timer.

thank you for you reply.


as you said, if the timer spent in the background is no different than time spent in the foreground. why the timer can not call its selector method in background

There two concepts you need to understand here:

  • Sleep — This is akin to what happens when you close the lid on your MacBook. The system goes into a very low power mode and that includes stopping the CPU, which means that Mach absolute time stops counting.

  • Suspend — When your app moves into the background, the system suspends your app, which prevents any code in your app from running. This is not an attribute of the system but an attribute of your app’s process. Suspending your app has no impact on Mach absolute time (because the system as a whole is still running).

While these two concepts are different, they are related. For example:

  • The system won’t sleep with your app in the foreground. But…

  • If you lock the screen, the system moves your app to the background. It then evaluates whether it can sleep.

  • The system can only sleep if there’s nothing preventing that. Various things can prevent system sleep, including:

    • An app with an outstanding

      UIApplication
      background task
    • An app running in the background, like a music or navigation app

    • Various system operations that prevent sleep

    This evaluation is done system wide.

  • Even if it doesn’t sleep, the system can still suspend your app. This is a separate evaluation that’s done just on your app.

This has a bunch of consequences:

  • If you set a timer for some time in the future and then stay in the foreground, the timer will always fire on time.

  • If you set a timer for some time in the future and then move to the background, the behaviour varies depending on context. If your app prevents suspension, using a

    UIApplication
    background task perhaps, it behaves just like the foreground case.
  • If not, the system may or may not sleep while your app is in the background. If it doesn’t sleep, then you’ll see one of three behaviours:

    • If your app is terminated while suspended, you’ll never get the timer callback.

    • Otherwise, if the timer fires while your app is suspended, your callback will run the next time you app is resumed (A).

    • If the timer fires after your app is resumed, your callback will run on time because the system hasn’t slept, and thus Mach absolute time never stopped) (B).

  • If the system does sleep, then the behaviours are similar except that in cases A and B your timer callback will run late because Mach absolute time stopped. Case A is particularly interesting, because that delay may or may not cause your callback to run later depending on how the delay caused by system sleep compares to the delay caused by your app being suspended.

*phew*

All of the above are fascinating implementation details but it doesn’t tell you how to write your code. And for that I’m going to come back to my 23 Jul post, which offers specific concrete advice that I continue to stand by.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

thank you for your reply


If the timer has been turned on, and switch to the background, and the iPhone is running, in fact the timer is still running, when the APP comes back to the front, we would get the timer callback.


the question is:

What determines if a callback of a timer cannot be called in the background ?

as you said

"

If the system does sleep, then the behaviours are similar except that in cases A and B your timer callback will run late because Mach absolute time stopped. Case A is particularly interesting, because that delay may or may not cause your callback to run later depending on how the delay caused by system sleep compares to the delay caused by your app being suspended.

"


how to understand "the delay caused by system sleep" and "the delay caused by your app" ?

and What's the difference?

Consider sequence A:

  1. Your app schedules a timer for time Tx.

  2. The user presses the Home button, causing your app to move to the background and thus be suspended.

  3. The user uses other apps for a while.

  4. At time T1, where T1 > Tx, the user brings your app to the front.

  5. Your app resumes, notices that the time has passed T1, and your timer fires.

In this case your timer has been delayed by T1 - Tx because your app was suspended.

Now consider sequence B:

  1. Your app schedules a timer for time Tx.

  2. The user presses the Sleep/Wake button. This causes your app to move to the background, shortly after which it’s suspended.

  3. There’s nothing else keeping the CPU awake, so it sleeps at time T1.

  4. At time T2, where T2 < Tx, the user presses the Home button, which wakes the CPU.

  5. Shortly thereafter they unlock the device, which resumes your app.

  6. Your app is in the foreground when Tx arrives, but your timer doesn’t fire.

  7. At time Tx + T2 - T1 your timer fires.

In this case your timer has been delayed by T2 - T1 because the CPU was stopped between T1 and T2, and thus Mach absolute time stopped counting.

So, in both A and B your timer is delayed, it’s just for different reasons. And that brings me back to the recommendations in my 23 Jul post. You should not rely on these implementation details, but should instead avoid these edge cases entirely.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

thank you for your reply


If the timer has been turned on, and switch to the background, and the iPhone is running, in fact the timer is still running, when the APP comes back to the front, we would get the timer callback.


the question is:

What determines if a callback of a timer cannot be called in the background ?

thank you for you reply:


Now consider follow sequence :

  1. Your app schedules a timer for time Tx.
  2. The user presses the Sleep/Wake button. This causes your app to move to the background, shortly after which it’s suspended.
  3. There’s nothing else keeping the CPU awake, so it sleeps at time T1.
  4. At time T2, where T2 > Tx, the user presses the Home button, which wakes the CPU.
  5. Shortly thereafter they unlock the device, which resumes your app.


In this case, can the timer fire after Tx + T2 - T1?