How to display time in seconds in Cocoa efficiently? - objective-c

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.

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.

Only trigger selector from applicationSignifigantTimeChangeNotification at midnight

I added an observer in my main view controller to get called when applicationsignifiganttimechange gets triggered, but I only want that to happen at midnight. By nature, applicationsignifiganttimechange happens when time format changes, and time zones changes, Etc. I need to know how I can focus it down to only at midnight.
Any thoughts?
Thanks.

How would you go about implementing a "dayDidChange" method, while avoiding the use of NSTimer?

How would you go about implementing a "dayDidChange" method?
I have a dateLabel, that needs updating, when the day changes.
I already had 2 solutions implemented. But gave up on them.
1st, on viewWillAppear, I would set my label. Chances for date changing when the user is viewing the VC are VERY small ... and it wouldn't really introduce any "errors" to execution But still, it's not perfect.
2nd, I also implemented an NSTimer with intervals of 1 second. Works great. But I'm not too found of the idea of having a method being called if there is a chance that I don't need to do it.
Are there any other options? I need to update that label, when the day changes. Also, is it possible to use NSNotificationCenter with this?
In your appliciation delegate, implement the following method: -(void)applicationSignificantTimeChange:(UIApplication *)application
This method is called when the day changes, or if the device's time has been changed in the background for whatever reason (such as changes to time zone).
hope it helps. happy coding :)
You can calculate the time left on the day and set a timer to it. You can also listen to UIApplicationSignificantTimeChangeNotification. Note that this notification will be fired not only when the date changes, but for timezone adjustments and other changes. Keep in mind also that the firing of the notification will be triggered only if your app is not in an inactive state.
There are many ways to do so, and I can elaborate more on them if needed.
But here is a nice quick trick
use this code in your app delegate
it will listen to time changes (when the date or time changes dramatically) and when that happens - run a test and update the label if needed.
read me about this here: https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIApplication_Class/Reference/Reference.html
It's just a quick simple way of doing it without timers.
- (void)applicationSignificantTimeChange:(UIApplication *)application {
NSLog(#"Time has changed, do a test and update the label if needed");
// [yourViewController runTimeTestAndUpdateFunction];
}

Timer that works in the background

For the sake of learning i'm making a timer. Used an NSTimer but it appears this stops
when going to the background.
Thought of using NSDate instead. Does this work in the background?
Do i compare current time to a set time to get the timer running properly?
Some thoughts would be greatly appreciated!
If you are looking for something to handle time based events in the background, have a look at UILocalNotification. With a local notification, the OS will alert the user at the appointed time whether the app that scheduled the notification is running or not.
If you are looking for something to happen when the app starts, you can use NSUserDefaults and store a value containing the current timestamp. Fire this off when in the AppDelegate inside applicationWillResignActive method. You can then check this value in the applicationDidBecomeActive.