NSTimer Not Firing in Subthread - objective-c

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 !

Related

Trying to deactive NSTimer

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];

UICollectionView sporadically crashes when NSTimer triggers reloadData and bouncing is enabled in iOS 7 on iPad

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.

How can I wait the while loop?

Here is my code:
float interval = 0.03;
while (interval > 0.005) {
timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
NSLog(#"%f", interval);
interval = interval - 0.005;
}
How can I make the while loop to wait an specific amount of time after every loop? I've been searching thing like sleep or performSelector:withObject:afterDelay but neither they don't work nor I don't know how to use them... Some help please?
You could use a recursive structure:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
[self recursiveMethode:[NSNumber numberWithDouble:0.3]];
}
-(void)onTimer
{
NSLog(#"timer");
}
-(void)recursiveMethode:(NSNumber *)interval
{
timer = [NSTimer scheduledTimerWithTimeInterval:interval.doubleValue target:self selector:#selector(onTimer) userInfo:nil repeats:YES];
NSLog(#"%f", interval.doubleValue);
double delay = 1;
[self performSelector:#selector(recursiveMethode:) withObject:[NSNumber numberWithDouble:interval.doubleValue - 0.05] afterDelay:delay];
}

Getting the spinner to begin as soon as view is loaded

in my app, I have a table in one of the viewControllers. When one of the rows are tapped, it takes the user to a different view/viewController and it works. Within this new viewController, data is being parsed from a php script in the background.
This takes about 7-10 seconds and in this time I want the user to IMMEDIATELY see a spinner that says "Loading..". I have implemented this myself but the spinner does not start to load until 4-5 seconds in. During this time, the screen is completely frozen and I cannot tap anything or go back until the spinner/data is displayed.
I have tried to put the following code within the method that does the actually fetching of the data, within both (not at the same time) the viewDidAppear and ViewDidLoad methods as well but the same thing happens.
If anyone knows how to fix this, it would be much appreciated.
Thank you
[web loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString:#"http://xxx.xx.xx.xxx/stuff/xxx.php"]]];
[web addSubview:spinner];
timer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector: #selector(tick) userInfo:nil repeats:YES];
load_message = [[UIAlertView alloc] initWithTitle:#"Loading..." message:nil delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
spinner= [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
spinner.center = CGPointMake(135.0, 60.0);
[load_message addSubview:spinner];
-(void) tick
{
if(!web.loading)
{
[spinner stopAnimating];
[load_message dismissWithClickedButtonIndex:0 animated:TRUE];
}
else {
[load_message show];
[spinner startAnimating];
}
}
You could start your spinner then start your long process after a delay:
- (void)someMethod
{
[spinner startAnimating];
[self performSelector#selector(doLongProcess:) withObject:someObject afterDelay:0.0];
}
- (void)doLongProcess:(id)someObject
{
//Some really long process
}
Your spinner was most likely blocked because a long process may be occurring on the same thread.
You have to do one of the following (assumes you put your web request in a method called 'startWebRequest'):
Start the UIActivityIndicatorView on the the main UI thread (if not already running from the main thread):
[self performSelectorOnMainThread:#selector(startWebRequest) withObject:nil waitUntilDone:NO];
Start the web request on a background thread:
[self performSelectorInBackground:#selector(startWebRequest) withObject:nil];
Pause like a 1/10th of second before starting the web request like with:
[NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:#selector(startWebRequest) userInfo:nil repeats:NO];

Objective-C help NSTimer doesn't repeat

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]]);