Weird behavior of NSTimer in class - ios

I have a real weird behavior of NSTimer object.
I'm trying to call ChangePic method with repeat counts of 2 seconds, But the timer isn't repeating. and i have this problem only in 1 class in the project
I have 5 View Controllers in my App, and the same code works in all classes except this one.
Does anybody has idea how could it be? is it possible that something blocking the timer ?
-BTW, ChangePic Method is called only once, and not repeating.
My Code:
ViewController.h
#property (nonatomic, strong) NSTimer *timer;
ViewController.m
_timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(ChangePic) userInfo:nil repeats:YES];
[_timer fire];
-(void) ChangePic {
NSLog(#"testing");
}

Assign it to the property not the backing variable.
self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:#selector(ChangePic) userInfo:nil repeats:YES];

It's difficult to say exactly what your issue is given that there isn't a whole lot of background given. I can tell you that I answered another question where the problem turned out to be the fact that one of the views was blocking the timer. Timers can run on a number of different modes and different scenarios can cause the timer to sometimes be blocked. Try adding the timer to all common modes like this:
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
Also, like #Hejazi said, remove the 'fire' method call. It's not needed on a scheduled timer.

Related

ns timer duplicate created 2-3 times

I am working on a app in which i have created a timer which called method make sound every 1 sec..
after 10 sec it is invalidated automatically but on one button click event i have stopped timer before 10 sec complete,but when again i come to that view it creates two timer and the call method twice and for third it called same method 3 times
DecilneTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(MakeSound) userInfo:nil repeats:YES];
and i have invalidate that like this on button click event
[DecilneTimer invalidate];
DecilneTimer=nil;
how to solve duplicate creation of timer???
You should use:
self.DecilneTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(MakeSound) userInfo:nil repeats:YES];
Using self.propertyName = ... you are using the property accessor and retaining the NSTimer automatically so that it doesnt get created again and again. And what you were doing was simply changing the ivar value directly

how to invalidate NSTimer?

self.timerProgress=[NSTimer scheduledTimerWithTimeInterval:50.0 target:self selector:#selector(stopProgressView) userInfo:nil repeats:NO];
-(void)stopProgressView
{
if ([self.timerProgress isValid]) {
[self.timerProgress invalidate];
self.timerProgress=nil;
}
}
and on a button click when i tried to invalidate NSTimer object
-(void)cancelTimer
{
if ([self.timerProgress isValid]) {
[self.timerProgress invalidate];
self.timerProgress=nil;
}
}
it don't get invalidate. It calls once stopProgressView after the interval of 50.
How to get resolve from this issue?
- (IBAction)stopTimer {
if ([timerProgress isValid]) {
[timerProgress invalidate];
}
}
Don't use self.timerProgress use just timerProgress
The most likely reason for this is that your timer scheduled on a different run loop to the one where you try and invalidate it.
Timers must be invalidated on the same thread/runloop as the run loop that they are scheduled on.
Cocoa touch isn't thread safe, so you should be running all UI related activities on the main thread. It may work if you do GUI work on different threads, but then you'll get random crashes, and you'll also generate timer problems like this.
It seems like from what you're posting it should work. This is how I have it in my apps and it works fine.
However, you could try making the selector one that takes a timer object like:
-(void)stopProgressView:(NSTimer *)timer{
//do stuff with timer here
}
Note that this would also mean that you should change #selector(stopProgressView) to #selector(stopProgressView:). Although for the record my current stop timer function just uses [self.timer invalidate] and it works fine.
My other piece of advice for debugging is to use NSLogs to make sure each of the methods are in fact getting called, and that when the method is called an NSLog within the if clause to make sure that works.
You create NSTimer with out NSRunLoop so your NSTimer not started, to add this code after
self.timerProgress = [NSTimer scheduledTimerWithTimeInterval:50.0
target:self
selector:#selector(stopProgressView)
userInfo:nil
repeats:NO];
//add
[[NSRunLoop currentRunLoop] addTimer:_tapTimer
forMode:NSDefaultRunLoopMode];

iOS NSTimer not working? [duplicate]

This question already has answers here:
NSTimer timerWithTimeInterval: not working
(4 answers)
Closed 8 years ago.
This is my exact code, and it doesn't seem to be working. Can you tell me what I am doing wrong? Note that refreshTimer was already declared in the private interface.
-(void)viewDidLoad {
refreshTimer = [NSTimer timerWithTimeInterval:1 target:self selector:#selector(timerTest) userInfo:nil repeats:YES];
}
-(void)timerTest {
NSLog(#"Timer Worked");
}
Give scheduledTimerWithTimeInterval a try:
NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(myMethod) userInfo:nil repeats:YES];
Quoting: NSTimer timerWithTimeInterval: not working
scheduledTimerWithTimeInterval:invocation:repeats: and scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: create timers that get automatically added to an NSRunLoop, meaning that you don't have to add them yourself. Having them added to an NSRunLoop is what causes them to fire.
There is two-option.
If using a timerWithTimeInterval
use a following like it.
refreshTimer = [NSTimer timerWithTimeInterval:1.0f target:self selector:#selector(timerHandler) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:refreshTimer forMode:NSRunLoopCommonModes];
also mode is two-option. NSDefaultRunLoopMode vs NSRunLoopCommonModes
more Information. refer a this documentation: RunLoopManagement
If using a scheduledTimerWithTimeInterval
use a following like it.
refreshTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(timerHandler) userInfo:nil repeats:YES];
scheduled timers are automatically added to the run loop.
more information. refer a this documentation: Timer Programming Topics
In summary
The "timerWithTimeInterval" you have to remember
to add the timer to the run loop that you want to add on.
The "scheduledTimerWithTimeInterval" default auto creates a timer that runs in
the current loop.

NSTimer periodic task doesn't get called while scrolling

I have an NSTimer
timer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(periodicTimer)
userInfo:nil
repeats:YES];
which does
- (void)periodicTimer
{
NSLog(#"Bang!");
if (timerStart != nil)
[timerLabel setText:[[NSDate date] timeDifference:timerStart]];
}
The problem is that while scrolling a tableview (or doing other tasks) the label doesn't get updated, furthermore, "Bang!" doesn't appear, so I supposed the method doesn't get called.
My question is how to update the label periodically even when the user is playing around with the app interface.
You'll need to add your timer to the UITrackingRunLoopMode to make sure your timer also fires during scrolling.
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
NSTimer *timer = [NSTimer timerWithTimeInterval:0.1 target:self selector:#selector(myTimerAction:) userInfo:nil repeats:YES];
[runloop addTimer:timer forMode:NSRunLoopCommonModes];
[runloop addTimer:timer forMode:UITrackingRunLoopMode];
From:
https://stackoverflow.com/a/1997018/474896
Not sure about this one, but my first guess would be that the main thread on which the interface is being rendered your timer just doesn't get a chance to do anything while its updating the interface.
You could create a new thread with a new run loop for your timer, but that is a bit of an ugly solution maybe. What functionality in your app are you trying to achieve? Maybe we can advise a better strategy than using a timer.

The selector of my NSTimer does not run. Why?

My code is:
-(void) timerRun{...}
-(void) createTimer
{
NSTimer *timer;
timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:#selector(timerRun)
userInfo:nil
repeats:YES];
}
viewDidLoad
{
[NSThread detachNewThreadSelector:#selector(createTimmer)
toTarget:self withObject:nil];
...
}
When I debug, the method createTimer runs ok, but the method does timerRun not run?
Just creating a timer doesn't start it running. You need to both create it and schedule it.
You're actually going to have to do slightly more work than that if you want it to run on a background thread. NSTimers attach to NSRunloops, which are the Cocoa form of an event loop. Each NSThread inherently has a a run loop but you have to tell it to run explicitly.
A run loop with a timer attached can run itself indefinitely but you probably don't want it to because it won't be managing autorelease pools for you.
So, in summary, you probably want to (i) create the timer; (ii) attach it to that thread's run loop; (iii) enter a loop that creates an autorelease pool, runs the run loop for a bit and then drains the autorelease pool.
Code will probably look like:
// create timer
timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:#selector(timerRun)
userInfo:nil
repeats:YES];
// attach the timer to this thread's run loop
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// pump the run loop until someone tells us to stop
while(!someQuitCondition)
{
// create a autorelease pool
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// allow the run loop to run for, arbitrarily, 2 seconds
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
// drain the pool
[pool drain];
}
// clean up after the timer
[timer invalidate];
You have to schedule a timer for it to run. They get attached to a run loop, which in turn updates the timer as necessary.
You can either change createTimer to
[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:#selector(timerRun)
userInfo:nil
repeats:YES];
or add
[[NSRunLoop currentRunLoop] addTimer:timer forModes:NSRunLoopCommonModes];
The method signature that you use in scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: must have an argument for the NSTimer as it passes itself as an argument.
You should change your message signature to:
(void)timerRun:(NSTimer *)timer;
You don't need to do anything with the argument, but it should be there. Also in createTimer the selector will become #selector(timerRun:) as it now accepts an argument:
timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:#selector(timerRun:)
userInfo:nil
repeats:YES];

Resources