How to access system time finer than 1 second on the iPhone - iphone

The system time function time(0) gives me a resolution of 1 second, right?
Is there a finer-grained function?
I'm using it to determine the time interval between two events.
A line of code would help me greatly. It makes it easier to have something concrete to hang the concept on when I look in the official documentation.

See CFAbsoluteTimeGetCurrent:
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
// do something you want to measure
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
NSLog(#"operation took %2.5f seconds", end-start);
Should you find CFAbsouteTime too verbose, you can simply use double instead.

NSDate has timeIntervalSinceDate: method, which returns double ("sub-millisecond precision over a range of 10,000 years" Apple says).
NSDate *start = [NSDate date];
…
NSTimeInterval duration = [[NSDate date] timeIntervalSinceDate:start];

Did you look for gettimeofday()? That's the main POSIX function for sub-second resolution timing analogous to time().
See Native App Development for the iPhone for an illustration of its use.

self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval
target:self selector:#selector(drawView) userInfo:nil
repeats:YES];
That's a snippet from the OpenGL app template. If you're looking for a high resolution timer, it's probably what you need.

Related

UILabel with current time

I have a UILabel that I want to show the current time (HH:mm) (the same time as in the status bar).
How do I update the label to change to the new time? If I schedule an NSTimer with an interval of 60 seconds, then label could be out of time by up to a minute, if the timer fires just before the system time's minute changes?
Would it be ok to set the timer's interval to 1 second, or will that use more resources than necessary? Or is there another way to make sure the label will stay in sync with the status bar clock (preferably exactly, but 1 second lee way is ok)?
Dispatch is your friend:
void runBlockEveryMinute(dispatch_block_t block)
{
block(); // initial block call
// get the current time
struct timespec startPopTime;
gettimeofday((struct timeval *) &startPopTime, NULL);
// trim the time
startPopTime.tv_sec -= (startPopTime.tv_sec % 60);
startPopTime.tv_sec += 60;
dispatch_time_t time = dispatch_walltime(&startPopTime, 0);
__block dispatch_block_t afterBlock = ^(void) {
block();
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60), dispatch_get_main_queue(), afterBlock);
};
dispatch_after(time, dispatch_get_main_queue(), afterBlock); // start the 'timer' going
}
This will synchronize down to the nanosecond and only call when the minute changes. I believe that this is the optimal solution for your situation.
Would it be ok to set the timer's interval to 1 second, or will that
use more resources than necessary?
Depends on what you're doing. If you're calculating the first million digits of pi, or rendering several hundred 3-D objects, you'll need every processor cycle you can spare. If the CPU is idling most of the time, you may as well use those cycles to make your interface look nice.
//Create a timer...
timer = [NSTimer scheduledTimerWithTimeInterval:0.25
target:self
selector:#selector(tick:)
userInfo:NULL
repeats:YES];
//Function to update time
- (void)tick:(NSTimer*)t
{
NSDate *now = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"HH:mm:ss"];
NSString *timeString = [dateFormatter stringFromDate:now];
[uilabel setText:timeString];
}
your timer will always be "delayed" by some time, since there is no "delegate" functions to call to make such feature.
I would stick with timer, but dispatch as Richard J. Ross III mentioned is valid as well.
I think I would run a NSTimer with a 1 second delay in a background thread, have the method it runs check the current time (HH:mm) to the current time displayed in the label. If it matches throw it away, if it's new, update the label.
If you are concerned about performance, after you return the first time, find out how many seconds away from the next minute it is, and have it run the timer run for that long. Then after the fist update have the timer run on 60 second intervals.
one second is the right answer.
Maybe you can set two timers. The first one (fired once) is used to synchronize the second timer with interval of 60s, which make it fired when the system time comes to HH:00;
Second-precision synchronised updates without gettimeofday or blocks.
Weak pointer and dispatch_async prevent retain cycle.
- (void)updateTimeLabel
{
if (!timeFormatter) {
timeFormatter = [NSDateFormatter new];
timeFormatter.dateStyle = NSDateFormatterNoStyle;
timeFormatter.timeStyle = NSDateFormatterShortStyle;
}
NSDate *currentTime = [NSDate date];
NSTimeInterval delay = [[currentTime nextMinute] timeIntervalSinceDate:currentTime];
timeLabel.text = [timeFormatter stringFromDate:currentTime];
__weak id weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf performSelector:#selector(updateTimeLabel) withObject:nil afterDelay:delay];
});
}
Get next minute with 0 seconds.
#implementation NSDate (Utils)
- (NSDate *)nextMinute {
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *comps = [calendar components:(NSCalendarUnit) NSUIntegerMax fromDate:self];
comps.minute += 1;
comps.second = 0;
return [calendar dateFromComponents:comps];
}
#end
I think there is a retain cycle with block in Richard's answer (it may be fixed with __weak+dispatch_async)

NSTimer does not fire in next date time

I need to fire timer on the basis of the time selected in the date picker.
Now if the selected time is greater than my current time then the NSTimeInterval returns positive value and the timer fired correctly.
My problem is when I select the time which is less than the current time then the NSTimeInterval returns negative value.
So Now what I does is :- add seconds so that it gets fired next day by 24*60*60.
The seconds value is coming correct because when I test this by changing the time into date it returns the next date with the selected time.
I use these seconds value in setting my timer.
Here is my code :
NSTimeInterval value=[[self.pickerView date] timeIntervalSinceDate:[NSDate date]];
value=value+24*60*60;
NSLog(#"value =%f",value);
NSDate *d1 = [NSDate dateWithTimeIntervalSinceNow:value];
NSLog(#"%#",d1);
NSDateFormatter *format=[[NSDateFormatter alloc]init];
[format setDateFormat:#"dd/MM/yyyy hh:mm:ss a"];
NSString *str=[format stringFromDate:d1];
NSLog(#"str=%#",str);
[format release];
NSTimer *t = [[NSTimer alloc] initWithFireDate: d1
interval: 1
target: app
selector:#selector(fireWakeUpTimerMethod:)
userInfo:dict repeats:NO];
// app.WakeUPTimer=t;
NSRunLoop *runner = [NSRunLoop currentRunLoop];
[runner addTimer:t forMode: NSRunLoopCommonModes];
[t release];
But the timer is not getting fired.
I tested it like this:- Suppose my device current time is 15 june 11:00 am then I select the 10:00 am time in picker view , since 10:00 am is less than 11:00 am then the time interval returns negative value so in this case I do value=value+(24*60*60); and set my timer with this value .Then I open the settings app and change my device date and time to 16 june 9:58 am and close settings app and open my app.But the method did not fire at 10:00 am.Please tell us if this is the correct way of testing this scenario.or I am doing some mistake in testing instead of coding.
Please help me as I am stuck here.
Thanks in advance!
I assume you are getting value correct:
value=value+(24*60*60);
Also check target of NSTimer and its selector

creating a timer for a level in iphone game

Im trying to add a timer to my game so that the user knows how long they have spent playing a level. Ive figured out that I can initialize a timer the following way:
bool showTimer = YES;
NSDate startDate;
UILabel timerLabel; // initialized in viewDidLoad
-(void) showElapsedTime: (NSTimer *) timer {
if (showTimer) {
NSTimeInterval timeSinceStart;
if(!startDate) {
startDate = [NSDate date];
}
timeSinceStart = [[NSDate date] timeIntervalSinceDate:startDate];
NSString *intervalString = [NSString stringWithFormat:#"%.0f",timeSinceStart];
timerLabel.text = intervalString;
if(stopTimer) {//base case
[timer invalidate];
}
}
}
- (void) startPolling {
[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(showElapsedTime:) userInfo:nil repeats:YES];
}
I start the startPolling method in the viewDidLoad. When I run the app, I do see the timer and it tracks the time but when I exit the app and re-enter it, the timer doesnt pause. I'm also not sure how to handle going to another view (like the options menu) and then coming back to this view. I understand NSDefaults and NSCoding and I see how I could save the current value on the timer as a Coding object, keeping a seperate key-value pair in a plist for every level but this seems cumbersome.
Is there a better way to keep track of how long the user spends in a level?
Instead of doing the calculation (subtracting the start time from the current time) every time, since all you care about is an elapsed time, just have a variable like NSTimeInterval elapsedTime that you start at 0 and add time to every time that the timer fires. (If you want to track it to 0.1 seconds like in your example, just divide by 10 before displaying it.) This way, you can pause it whenever you want and it will just continue on from where it was before when it starts up again.

Strange delay on NSTimer

I am having the following code in viewDidLoad method. All it does is updating a label every second. However when I run this code it shows the dummy text, waits a bit then it shows 8 sec instead of 9 then 8..
So it seems like it is skipping 9, is it possible to fix this? Should I have rounded the decimals when I calculate the time left?
Thank you for your time!
//Just dummy text
self.lblTime.text = #"Tid: 0h 0min 10sec";
NSDate *expireDate = [[NSDate alloc] initWithTimeInterval:10 sinceDate:[NSDate date]];
self.expires = expireDate;
[expireDate release];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(updateTimerDisplay) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:UITrackingRunLoopMode];
In the updateTimerDisplay I have:
//Gets the time right now
NSDate *now = [NSDate date];
//Stores the difference in seconds between when the test was started and now.
NSTimeInterval interval = [self.expires timeIntervalSinceDate:now];
//Gets the number of hours
NSInteger hours = interval / 3600;
interval = (int)interval % 3600;
//Gets the number of seconds
NSInteger minutes = interval / 60;
interval = (int)interval % 60;
NSInteger seconds = interval;
//Updates the label
self.lblTime.text = [NSString stringWithFormat:#"Time: %dh %dmin %dsec", hours, minutes, seconds];
NSTimer is not guaranteed to fire exactly at the correct interval. The only thing you can be sure of is that the timer does not fire too early (assuming your device's clock runs at an accurate speed), but it is very possible that it fires too late.
In your case, even if the timer fires only a little too late, you will get a wrong result because you are not rounding your seconds value, you just truncate it. You should consider rounding seconds and/or having the timer fire more frequently (e.g. every 0.2 or 0.1 seconds).
Firstly, I'd recommend using [NSDate dateWithTimeIntervalSinceNow:10] instead of [[NSDate alloc] initWithTimeInterval:10 sinceDate:[NSDate date]] and [self.expires timeIntervalSinceNow] instead of [self.expires timeIntervalSinceDate:[NSDate date]]. These are more succinct.
Also remember that since self.expires is an earlier date than now in your second set of code, interval will be negative, because the interval since now is really the interval since self.expires. Your code doesn't appear to take this fact into account.
Finally, If you set expires to be 10 seconds into the future, and then get your timer to fire in a second, you may well have missed your 9 second mark. This is because all timers are inherently not 100% accurate, and so slightly more than 1 second may have elapsed. Say 1.01 seconds has elapsed, then that leaves 10 - 1.01 = 8.99 seconds remaining, which using integer math will be truncated to 8 seconds remaining. This seems to me to cause of your error. I'd generally recommend firing your timer more frequently that every second (every tenth of a second or so) to avoid these problems.
If you're interested, this is an example of temporal aliasing.

iPhone Add a timer at Navigation Bar

HI , i have made simple application with 5 view controllers with some functionality .. what i want to do now is add a time at the main screen . and it should b running till i quit from application .. i will move to other view controllers also but that timer would b running .. how i will have this functionality ??
Check out the "Timers" section here: http://www.iphoneexamples.com/
Also, refer to Apple's NSTimer Documentation
The most practical way to do this is to fake it - that is, just store the start timestamp, and don't bother to continuously maintain any kind of timePassed variable. This is both easier to code, and actually more reliable since it's stable.
Store an NSDate for the instant the timer was started, and whenever you want to display or update the timer, use NSDate's timeIntervalSinceNow method, which returns the number of seconds passed as an NSTimeInterval, which is basically a typedef for a double. Note: this function returns a negative number when called on a timestamp in the past, which will be the case here.
If part of your display is showing this time, you can update it every second (or even more often) with an NSTimer object that periodically called one of your methods which updates the display.
Sample code:
// In the initialization code:
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self
selector:#selector(secondPassed:) userInfo:nil repeats:YES];
// Later:
// (This code assumes #minutes < 60.)
- (void) secondPassed: (NSTimer:) timer {
NSTimeInterval secondsPassed = -1 * [self.timerStart timeIntervalSinceNow];
int minutes = (int)(secondsPassed / 60);
int seconds = (int)(seconds - minutes * 60);
NSString* timerString = [NSString stringWithFormat:#"%02d:%02d",
minutes, seconds];
[self updateTimerDisplay:timerString];
}