I have a Foundation CLI tool that uses 5 NSTimers to execute different methods in the background. Ever since implementing these timers (rather than a while loop or similar), I've noticed the CPU usage of the program skyrocket. Does anyone have any information on this?
EDIT:
Here's a code snippet (only 3 timers are currently implemented):
[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:#selector(executeInterval0Update) userInfo:nil repeats:YES];
[NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(executeInterval1Update) userInfo:nil repeats:YES];
[NSTimer scheduledTimerWithTimeInterval:10 target:self selector:#selector(executeInterval2Update) userInfo:nil repeats:YES];
[runLoop run];
Related
I am using NSTimer to run a loop for counting down, then at some point(clicking 'start') I need to run this once again. The first Timer isn't stopped though so countdown pace increases with every run of the function
- (IBAction)startbutton:(id)sender {
timeTick = arc4random_uniform(7)+3;
timeofstart = CACurrentMediaTime();
chosentime = timeTick;
NSLog(#"chosentime værdien er %f", chosentime);
[timer invalidate];
timer = nil;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(tick) userInfo:nil repeats:YES];
}
- (void) tick {
if (timeTick == 0) {
winnerlabel2.text = #"GO !!!";
}
else {
timeTick --;
NSLog(#"%f", timeTick);
}
}
Here, it is unclear what timer is referring to in these first 2 lines. This 3rd line is creating a new local variable. It looks like you must have an instance variable timer that is created somewhere else. You are invalidating that timer, but then creating a new timer as a local variable that has no permanent handle (because it dies after going out of scope at the end of this method call) and thus every timer after the first never gets invalidated.
[timer invalidate];
timer = nil;
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(tick) userInfo:nil repeats:YES];
Change this 3rd line from...
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(tick) userInfo:nil repeats:YES];
to...
timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(tick) userInfo:nil repeats:YES];
Am I doing something wrong or there is a problem with the system?
Very simple demo: https://github.com/IgorTavcar/UICollectionViewBug.
Here is a collection view and a periodic trigger, started by
- (void)viewDidAppear:(BOOL)animated {
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(tick) userInfo:nil repeats:TRUE];
}
Every tick invokes the roladData of collection view.
- (void)tick {
[self.collectionView reloadData];
}
If scroll view
#property(nonatomic) BOOL bounces
is TRUE
then application crashes with EXC_BAD_ACCESS after max. 15 seconds of intensive scrolling /bouncing/.
Any suggestions?
I've also tried to do the
dispatch_async(dispatch_get_main_queue(), ^{
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:#selector(tick) userInfo:nil repeats:TRUE];
});
and
self.timer = [NSTimer timerWithTimeInterval:0.5 target:self selector:#selector(tick) userInfo:nil repeats:TRUE];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
...
Fixed:
https://github.com/IgorTavcar/UICollectionViewBug/issues/1
Uncheck "User Interaction Enabled" and "Multiple Touch" from the Collection View Cell in the Story Board.
Use a gesture recognizer to handle it input instead.
I am trying to get a NSTimer to fire in a subthread. My code essentially looks like this:
-(void) handleTimer:(NSTimer *)theTimer {
NSLog(#"timer fired");
}
-(void) startMyThread {
// If I put timer in here, right before I start my new thread, it fires.
// [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(handleTimer:) userInfo:nil repeats:NO];
[NSThread detachNewThreadSelector:#selector(run) toTarget:self withObject:nil];
}
// This method is called from inside the new thread
-(void) playNote:(Note *)theNote withTemplate:(NSString *)theTemplate X:(int)x andY:(int)y {
NSLog(#"before timer");
// The timer doesn't fire in here. (But the print statements do.)
[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(handleTimer:) userInfo:nil repeats:NO];
NSLog(#"after timer");
}
I would really like to (read:need to?) fire the timer in the subthread because it is going to be used to stop notes from playing (and all of the note playing needs to happen in a subthread).
I must be missing something with how a NSTimer runs in subthreads...
Premature thanks for the help!
A timer won't fire unless it is scheduled in a run loop. You'll also want to avoid spinning up threads willy-nilly.
Unless your run method fires up a run loop, your timer won't fire.
A better solution, btw, might be to use GCD's dispatch_after(). It is lighter weight than a thread, but it depends on what you need to do. In general, though, queues are a more efficient means of adding concurrency to an app.
Grabbing the code from the comment for proper formatting (good find):
double delayInSeconds = .2;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
NSLog(#"dispatch popped");
});
One additional note; you might consider using one of the global concurrent queues. They won't block when the Main Event Loop might block. Of course, if you are updating the UI, you have to go back to the MEL anyway.
You should add the NSTimer to currentRunLoop. Your playNote method should look like this:
-(void) playNote:(Note *)theNote withTemplate:(NSString *)theTemplate X:(int)x andY:(int)y
{
NSLog(#"before timer");
// The timer doesn't fire in here. (But the print statements do.)
NSTimer myTimer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(handleTimer:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:myTimer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
NSLog(#"after timer");
}
That should do it :)
Add the timer to the current runloop, and run.
-(void) handleTimer:(NSTimer *)theTimer {
NSLog(#"timer fired");
}
-(void) startMyThread {
// If I put timer in here, right before I start my new thread, it fires.
// [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(handleTimer:) userInfo:nil repeats:NO];
[NSThread detachNewThreadSelector:#selector(run) toTarget:self withObject:nil];
}
// This method is called from inside the new thread
-(void) playNote:(Note *)theNote withTemplate:(NSString *)theTemplate X:(int)x andY:(int)y {
NSLog(#"before timer");
// The timer doesn't fire in here. (But the print statements do.)
NSTimer *Timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(handleTimer:) userInfo:nil repeats:NO];
NSLog(#"after timer");
[[NSRunLoop currentRunLoop] addTimer:Timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}
Try to addTimer: within the [NSRunLoop mainRunLoop], not the current one !
I'm trying to implement retry logic with exponential backoff using NSTimer.
My code looks like this:
-(void)start
{
[NSTimer scheduledTimerWithTimeInterval:0.0 target:self
selector:#selector(startWithTimer:) userInfo:nil repeats:NO];
}
-(void)startWithTimer:(NSTimer *)timer
{
if (!data.ready) {
// timer.timeInterval == 0.0 ALWAYS!
NSTimeInterval newInterval = timer.timeInterval >= 0.1 ? timer.timeInterval * 2 : 0.1;
newInterval = MIN(60.0, newInterval);
NSLog(#"Data provider not ready. Will try again in %f seconds.", newInterval);
NSTimer * startTimer = [NSTimer scheduledTimerWithTimeInterval:newInterval target:self
selector:#selector(startWithTimer:) userInfo:nil repeats:NO];
// startTimer.timeInteval == 0.0 ALWAYS!
return;
}
...
}
The problem I'm having is that the timer NSTimer scheduledTimerWithTimeInterval seems to ignore the interval I'm providing and always sets it to 0.0. Any suggestions on what I'm doing wrong here?
The Apple Documentation has this to say about the timeInterval property on NSTimer.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nstimer_Class/Reference/NSTimer.html
If the receiver is a non-repeating timer, returns 0 (even if a time interval was set).
You will need to use some other means to keep track of what the timer interval should be. I recommend an iVar on your class.
-(void)start
{
_timeInterval = 0.0;
[NSTimer scheduledTimerWithTimeInterval:_timeInterval target:self
selector:#selector(startWithTimer:) userInfo:nil repeats:NO];
}
-(void)startWithTimer:(NSTimer *)timer
{
if (!data.ready) {
_timeInterval = _timeInterval >= 0.1 ? _timeInterval * 2 : 0.1;
_timeInterval = MIN(60.0, _timeInterval);
NSLog(#"Data provider not ready. Will try again in %f seconds.", _timeInterval);
NSTimer * startTimer = [NSTimer scheduledTimerWithTimeInterval:_timeInterval target:self
selector:#selector(startWithTimer:) userInfo:nil repeats:NO];
return;
}
...
}
i need a little help i have a method; countDown which is called when iTunes sends a notification, the method countDown then runs the method timerHit which gets a double minuses one from it then sets the value to a label, the method countDown is set to repeatedly run timerHit, however it doesn't seem to be working.
Here's what i have so far, any help would be much appreciated.
- (void)countDown {
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(timerHit:) userInfo:nil repeats:YES];
}
- (void)timerHit:(NSTimer *)p_timer {
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
if ([iTunes isRunning]) {
double trackDuration = [[iTunes currentTrack] duration];
trackDuration--;
[duration setDoubleValue:trackDuration];
}
}
Thanks, Sami.
If the timer is on a thread then you should run it on a active run loop like so:
NSRunLoop *mLoop = [NSRunLoop currentRunLoop];
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(timerHit:) userInfo:nil repeats:YES];
mRunLoop = YES;
while (mRunLoop && [mLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]);