while(...condition...)
{
//do something
NSDate *date = [NSDate date];
NSTimeInterval milliseconds = [date timeIntervalSince1970];
[NSThread sleepForTimeInterval:0.2];
date = [NSDate date];
NSTimeInterval milliseconds1 = [date timeIntervalSince1970];
NSLog(#"**** time taken : %f",milliseconds1-milliseconds);
//calling some method
}
After 2 minutes of execution of this loop, the "time taken" increases from 200ms to 10s. Why? What is the problem ?
Assuming you are running in a multithreaded environment, answering your question would require a lot more information than you are giving us. Suffice to say, there is no guarantee that your sleeping thread will run "calling some method" exactly 200ms later, because that depends on what your other threads are doing.
Sleeping a thread like your example is generally considered a bad idea. Here's another way to accomplish what I think you are trying to do, but with ... better citizenship.
- (void)loopIfNeeded
{
if (...condition...) {
// do something
// ... and then call -someMethod ~200ms later
[self performSelector:#selector(someMethod) withObject:nil afterDelay:0.2];
}
}
- (void)someMethod
{
// whatever some method does
[self loopIfNeeded]; // continue loop
}
Finally, some observations:
-[NSDate timeIntervalSince1970] returns seconds (and fractions thereof, docs here), not milliseconds (although it has ms precision).
For timing, I find CFAbsoluteTime to be lighter weight:
CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
// do your thing
CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
CFTimeInterval elapsed = start - end; // or just NSLog(#"elapsed %f", start - end);
The general comments being made suggesting this is not the way to pause your loop are correct, you should look at other ways to do this - using blocks, GCD and routines such as dispatch_after is one approach.
However, to answer your actual question: sleeping is imprecise, you sleep for at least the time specified but maybe longer. E.g. from the sleep(3) manual page:
System activity may lengthen the sleep by an indeterminate amount.
Having said that, extend 0.2s to 10s does seem quite a long stretch. Look for other activity that might be kicking in and slowing you down, if you are on Mavericks consider its aggressive actions (app nap, timer coalescing, etc.).
But really, unless this is just test code look at partitioning it into A: test condition do part 1 and B: do part 2 then call A, or some other way, and use a timer callback or GCD to handle the pause.
HTH
Your code is wrong!!!
while(...condition...)
{
//do something
NSDate *date = [NSDate date];
NSTimeInterval milliseconds = [date timeIntervalSince1970];
[NSThread sleepForTimeInterval:0.2];
date = [NSDate date];
NSTimeInterval milliseconds = [date timeIntervalSince1970]; // here should be milliseconds1
NSLog(#"**** time taken : %f",milliseconds1-milliseconds);
//calling some method
}
You're messing up your variables milliseconds and milliseconds1.
Related
I'm working with an app that processes device motion events and updates interface in 5 second increments. I would like to add an indicator to the app that would display the total time the app has been running. It seems that a stopwatch-like counter, like the native iOS Clock app is a reasonable way to count time that the app has been running and display it to the user.
What I'm not sure of is the technical implementation of such a stopwatch. Here's what I'm thinking:
if I know how long between interface updates, I can add up seconds between events and keep a count of seconds as a local variable. Alternatively, a 0.5 second interval scheduled timer can provide the count.
If I know the start date of the app, I can convert the local variable to date for each interface update using [[NSDate dateWithTimeInterval:(NSTimeInterval) sinceDate:(NSDate *)]
I can use a NSDateFormatter with a short time style to convert the updated date to a string using stringFromDate method
The resulting string can be assigned to a label in the interface.
The result is that the stopwatch is updated for each "tick" of the app.
It appears to me that this implementation is a bit too heavy and is not quite as fluid as the stopwatch app. Is there a better, more interactive way to count up time that the app has been running? Maybe there's something already provided by iOS for this purpose?
If you look in the iAd sample code from Apple in the basic banner project they have a simple timer:
NSTimer *_timer;
_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
and the the method they have
- (void)timerTick:(NSTimer *)timer
{
// Timers are not guaranteed to tick at the nominal rate specified, so this isn't technically accurate.
// However, this is just an example to demonstrate how to stop some ongoing activity, so we can live with that inaccuracy.
_ticks += 0.1;
double seconds = fmod(_ticks, 60.0);
double minutes = fmod(trunc(_ticks / 60.0), 60.0);
double hours = trunc(_ticks / 3600.0);
self.timerLabel.text = [NSString stringWithFormat:#"%02.0f:%02.0f:%04.1f", hours, minutes, seconds];
}
It just runs from start up, pretty basic.
Almost what #terry lewis suggested but with an algorithm tweak:
1) schedule a timer
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(timerTick:) userInfo:nil repeats:YES];
2) when the timer fires, get the current time (that's the tweak, don't count ticks because if there is wobble in the timer, tick counting will accumulate the error), then update the UI. Also, NSDateFormatter is a simpler and more versatile way to format time for display.
- (void)timerTick:(NSTimer *)timer {
NSDate *now = [NSDate date];
static NSDateFormatter *dateFormatter;
if (!dateFormatter) {
dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"h:mm:ss a"; // very simple format "8:47:22 AM"
}
self.myTimerLabel.text = [dateFormatter stringFromDate:now];
}
I have a timer that I start and stop multiple times. How do I accumulate the values once the timer has stopped each time?
When the timer is started note the start time. Whenever the timer fires add the difference between the current time and the start time to your total. Then reset the start time to the current time. When the timer is stopped, you either add the difference between the current time and start time to the total or don't depending on your application, but you don't reset the start time. When the timer is restarted, don't add anything to the total, but reset the start time.
So something like this:
startTime = [[NSDate alloc] initWithTimeIntervalSinceNow]; // This is an NSDate* in your class
Then when your timer fires:
NSDate* currentTime = [NSDate dateWithTimeIntervalSinceNow];
accumulatedTime += [currentTime timeIntervalSinceDate:startTime]; // accumulatedTime is an NSTimeInterval in your class
[startTime release];
startTime = [currentTime retain];
etc.
I figured it out... here is how I did it:
NSTimeInterval *accumulatedTime;
accumulatedTime += 1.0; // add one second
This is assuming of course that you have the supporting code to start, pause and stop the timer.
I'm trying to calculate (in milliseconds) how much time went by while completing operations in my app. I've read through most of the documentation and looked through examples, although my app seems to crash near the NSLog function. All of the answers addressed here seem to be specifically for iOS, and I'm trying to get this working with OSX. Any help or suggestions would be appreciated — thank you.
- (void)someMethod {
NSTimeInterval startTime = [[NSDate date] timeIntervalSinceReferenceDate];
// Do something else
// Now see how much time went by
NSTimeInterval endTime = [[NSDate date] timeIntervalSinceReferenceDate];
double elapsedTime = startTime - endTime;
NSLog(#"time: %#", elapsedTime);
}
An alternative way to do it would be to grab the start NSDate. Then to find time elapsed, call [startDate timeIntervalSinceNow].
Your problem is that you are trying to print an object with %# but the time is a double. Instead you should change the last line to
NSLog(#"time: %f", elapsedTime);
Also note that startTime - endTime give you a negative time so you should probably switch the order to get the correct duration.
I'm looking to create a countdown timer for SMPTE Timecode (HH:MM:SS:FF) on iOS. Basically, it's just a countdown timer with a resolution of 33.33333ms. I'm not so sure NSTimer is accurate enough to be counted on to fire events to create this timer. I would like to fire an event or call a piece of code every time this timer increments/decrements.
I'm new to Objective-C so I'm looking for wisdom from the community. Someone has suggested the CADisplayLink class, looking for some expert advice.
Try CADisplayLink. It fires at the refresh rate (60 fps).
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:#selector(timerFired:)];
displayLink.frameInterval = 2;
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
This will fire every 2 frames, which is 30 times per seconds, which seems to be what you are after.
Note, that this is tied to video frame processing, so you need to do your work in the callback very quickly.
You basically have no guarantees with either NSTimer or dispatch_after; they schedule code to triggered on the main thread, but if something else takes a long time to execute and blocks the main thread, your timer won't fire.
That said, you can easily avoid blocking the main thread (use only asynchronous I/O) and things should be pretty good.
You don't say exactly what you need to do in the timer code, but if all you need to do is display a countdown, you should be fine as long as you compute the SMPTE time based on the system time, and not the number of seconds you think should have elapsed based on your timer interval. If you do that, you will almost certainly drift and get out of sync with the actual time. Instead, note your start time and then do all the math based on that:
// Setup
timerStartDate = [[NSDate alloc] init];
[NSTimer scheduledTimer...
- (void)timerDidFire:(NSTimer *)timer
{
NSTImeInterval elapsed = [timerStartDate timeIntervalSinceNow];
NSString *smtpeCode = [self formatSMTPEFromMilliseconds:elapsed];
self.label.text = smtpeCode;
}
Now you will display the correct time code no matter how often the timer is fired. (If the timer doesn't fire often enough, the timer won't update, but when it updates it will be accurate. It will never get out of sync.)
If you use CADisplayLink, your method will be called as fast as the display updates. In other words, as fast as it would be useful, but no faster. If you're displaying the time, that's probably the way to go.
If you are targeting iOS 4+, you can use Grand Central Dispatch:
// Set the time, '33333333' nanoseconds in the future (33.333333ms)
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 33333333);
// Schedule our code to run
dispatch_after(time, dispatch_get_main_queue(), ^{
// your code to run here...
});
This will call that code after 33.333333ms. If is this going to be a loop sorta deal, you may want to use the dispatch_after_f function instead that uses a function pointer instead of a block:
void DoWork(void *context);
void ScheduleWork() {
// Set the time, '33333333' nanoseconds in the future (33.333333ms)
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 33333333);
// Schedule our 'DoWork' function to run
// Here I pass in NULL for the 'context', whatever you set that to will
// get passed to the DoWork function
dispatch_after_f(time, dispatch_get_main_queue(), NULL, &DoWork);
}
void DoWork(void *context) {
// ...
// Do your work here, updating an on screen counter or something
// ...
// Schedule our DoWork function again, maybe add an if statement
// so it eventually stops
ScheduleWork();
}
And then just call ScheduleWork(); when you want to start the timer. For a repeating loop, I personally think this is a little cleaner than the block method above, but for a one time task I definitely prefer the block method.
See the Grand Central Dispatch docs for more info.
I am pausing an NSTimer like so:
- (IBAction)pause
{
pauseStart = [[NSDate dateWithTimeIntervalSinceNow:0] retain];
previousFiringDate = [stopmusictimer fireDate];
NSDate *date = [NSDate distantFuture];
[stopmusictimer setFireDate:date];
}
I have another IBAction:
- (IBAction)unpausebutton
{
// dunno code
}
But I am not sure how to unpause the NSTimer now. Anyone, please help me! Thanks
NSTimer instances weren't really designed to be dynamically mucked with; the setFireDate: explicitly documents that doing so is relatively expensive. Probably similar to just invalidating the old one and creating a new one.
If you are pausing the timer and then your "unpause" is always "fire right now", I'd suggest invalidating and releasing the timer on pause (release only if necessary) then simply calling the targeted method on unpause.
That would be a more typical pattern.