NSTimer not stopping? - iphone

I'm trying to to stop an NSTimer with the following code:
- (void)viewDidLoad
{
[super viewDidLoad];
timer3 = [NSTimer timerWithTimeInterval:5.0 target:self selector:#selector(start) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer3 forMode:NSDefaultRunLoopMode];
}
-(void)invalidate
{
[timer3 invalidate];
timer3 = nil;
}
and I call -(void)invalidate from another class like this:
-(void)timer
{
ClassOfMyTimer *class = [[ClassOfMyTimer alloc] init];
[class invalidate];
}
but the timer doesn't stop. Does anyone know what I'm doing wrong?

You need to call your invalidate method on the same instance of your class that created the timer. In your timer method you create a new instance of your class which could have its own timer and invalidate that.

I'm kind of confused by what you're trying to do here, but I'd guess that you're not maintaining a reference to timer3.
Have you created a property in the .h file for the timer:
#property (strong) NSTimer *timer3;
And then added a synthesize statement in the .m file:
#synthesize timer3;
Then, in viewDidLoad:, you can maintain a reference to the timer you're creating via:
self.timer3 = [[[NSTimer timerWithTimeInterval:5.0 target:self selector:#selector(start) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.timer3 forMode:NSDefaultRunLoopMode];
And, to invalidate the timer later:
[self.timer3 invalidate]
self.timer3 = nil
On preview, Sven also has a valid solution to an issue that might be impacting you..

Related

NSTimer Delegate Selection

I am trying to create a framework for all of the custom objects and views that I have made and use often, by creating custom delegate classes and custom objects. Everything has gone well except when trying to get NSTimers to call the correct method inside of the delegate class.
Here is the basic setup.
-(void) startTimers {
NSTimer *timer1 = [NSTimer timerWithTimeInterval:5 target:self selector:#selector(doSomething:) userInfo:nil repeats:YES];
NSTimer *timer2 = [NSTimer timerWithTimeInterval:5 target:self selector:#selector(doSomethingElse:) userInfo:nil repeats:YES];
}
I can easily just call this method and whatever, but when this time fires it does not call the method I defined as the selector. I am pretty sure it has something to do with the delegate value and which class it is making as the delegate.
Note the file I am writing in is a subclass of UIView, which is set up to be a delegate using the #protocol tags and all of that.
What should I set as the target when defining my timers to get them to call the correct methods.
EDIT:
Here is an example of what I am doing:
ExampleView.h
#import <UIKit/UIKit.h>
#protocol ExampleViewDelegate;
#interface ExampleView : UIView {
NSTimer *timer;
}
-(void) initWithStuff:(id)stuff andFrame:(CGRect)frame;
-(void) testTimer;
#end
#protocol ExampleViewDelegate
-(void) someDelegateFunction;
#end
ExampleView.m
#import "ExampleView.h"
#implementation ExampleView
-(id) initWithStuff:(id)stuff andFrame:(CGRect)frame {
self = [super initWithFrame:frame];
timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(testTimer) userInfo:nil repeats:YES];
return self;
}
-(void) testTimer {
NSLog(#"Timer Fired");
}
#end
If you add this custom view into a viewcontroller it will never call that testTimer function and print "Timer Fired" So what I am thinking is that when I set the delegate for this timer, it is actually setting it to something else. Any ideas?
NSTimer *timer1 = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(doSomething:) userInfo:nil repeats:YES];
Notice the method is called "scheduledTimerWithTimeInterval"

how to unschedule NSTimer in objective-c

I am using nested NSTimer in an application. I have two issues here.
How to re-initiate time counter in this function - (void)updateLeftTime:(NSTimer *)theTimer
How to kill previous timer because - (void)updateLevel:(NSTimer *)theTimer is also calling by timer.
- (void)viewDidLoad {
[super viewDidLoad];
tmLevel=[NSTimer scheduledTimerWithTimeInterval:20.0f target:self selector:#selector(updateLevel:) userInfo:nil repeats:YES];
tmLeftTime=[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateLeftTime:) userInfo:nil repeats:YES];
}
- (void)updateLevel:(NSTimer *)theTimer {
static int count = 1;
count += 1;
lblLevel.text = [NSString stringWithFormat:#"%d", count];
tfLeftTime.text=[NSString stringWithFormat:#"%d",ANSWER_TIME];
tmLeftTime=[[NSTimer alloc] init];
tmLeftTime=[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateLeftTime:) userInfo:nil repeats:YES];
[self playMusic];
}
- (void)updateLeftTime:(NSTimer *)theTimer {
static int timeCounter=1;
timeCounter+=1;
tfLeftTime.text=[NSString stringWithFormat:#"%d", (ANSWER_TIME-timeCounter)];
}
Use [tmLevel invalidate] to cancel schedule of a timer.
Don't forget to set tmLevel=nil immediately after (to avoid using the variable after the timer has been unscheduled and released by the Runloop)
Don't forget to invalidate the tmLevel timer before loosing the reference to it, namely call [tmLevel invalidate] also before assigning a new NSTimer to the tmLevel variable (or else the previous timer will continue to run in addition to the new one)
Note also that in your code you have useless allocations that are moreover creating a leak:
tmLeftTime=[[NSTimer alloc] init];
tmLeftTime=[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateLeftTime:) userInfo:nil repeats:YES];
here you allocate an NSTimer instance, store this instance in tmLeftTime... and then immediately forget about this created instance to replace it with another one, created using [NSTimer scheduledTimerWithTimeInterval:...]!
Therefore, the NSTimer created using [[NSTimer alloc] init] is lost, and is creating a leak (as it will never be released).
Your first line is totally useless, it's kinda like you were doing
int x = 5;
x = 12; // of course the value "5" is lost, replaced by the new value
add the following lines when u want to reset the timer
[tmLeftTime invalidate];
tmLeftTime = nil;
you can also use
if ([tmLeftTime isValid]){
// the timer is valid and running, how about invalidating it
[tmLeftTime invalidate];
tmLeftTime = nil;
}
How about using only one timer instead of 3?
- (void)viewDidLoad {
[super viewDidLoad];
tmLeftTime=[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateLeftTime:) userInfo:nil repeats:YES];
}
- (void)updateLevel {
static int count = 1;
count += 1;
lblLevel.text = [NSString stringWithFormat:#"%d", count];
tfLeftTime.text=[NSString stringWithFormat:#"%d",ANSWER_TIME];
[self playMusic];
}
- (void)updateLeftTime:(NSTimer *)theTimer {
static int timeCounter=1;
timeCounter+=1;
tfLeftTime.text=[NSString stringWithFormat:#"%d", (ANSWER_TIME-timeCounter)];
if (timeCounter >= ANSWER_TIME) {
timeCounter = 0;
[self updateLevel];
}
}
Invalidate your timer with the invalidate method in your updateLevel: method and re-schedule the same timer.
[tmLevel invalidate];
tmLevel = [NSTimer scheduledTimerWithTimeInterval:20.0f target:self selector:#selector(updateLevel:) userInfo:nil repeats:YES];
And if you wanna call the updateTimeLeft: method you don't need to alloc another timer, that's a big leak since you're never releasing those references.
tmLeftTime = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateLeftTime:) userInfo:nil repeats:YES];
And in your updateTimeLeft: just re-schedule the timer's method and set a condition where it should stop.
tmLeftTime = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(updateLeftTime:) userInfo:nil repeats:YES];

Stopping a 'performSelector afterDelay' before it fires

I start a repeating NSTimer after a 4 second delay using the following code:
- (void)viewDidLoad {
[self performSelector:#selector(startTimer) withObject:self afterDelay:4];
}
- (void)startTimer {
NSTimer *mytimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(doSomething) userInfo:nil repeats:YES];
}
- (void)doSomething {
NSLog(#"What up!");
}
Problem is I may need to cancel startTimer from being called before the 4 seconds is up. Is there a way of doing this? I'd actually prefer to not use the performSelector in the first place (seems messy). If only NSTimer had something along the lines of this…
NSTimer *mytimer = [NSTimer
scheduledTimerWithTimeInterval:1.0
afterDelay:4.0 target:self
selector:#selector(doSomething)
userInfo:nil repeats:YES];
…then that would be perfect as I could just call the following:
[myTimer invalidate];
Any help or tips are much appreciated =)
P.S. I've found something called cancelPreviousPerformRequestsWithTarget in the NSObject class reference. Doesn't seem to be a method I can call from where this code runs however. If that's getting back on the right track your feedback is welcome!
Plz go through the SP post link
Stopping a performSelector: from being performed
[NSObject cancelPreviousPerformRequestsWithTarget:self
selector:#selector(sr)
object:nil];
The documentation for -performSelector:withObject:afterDelay: points you to the methods for canceling a queued perform request.
[myTimer invalidate] doesn't work?
Just keep a track of the object in your class, or in a centralized store for example.
If you do so, you could access your timer from everywhere you want, and invalidate it whenever it is needed
Use the NSTimer to fix issue.
self.autoTimer = [NSTimer timerWithTimeInterval:3.0 target:self
selector:#selector(connectionTimeout:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:autoTimer
forMode:NSDefaultRunLoopMode];
and call when you want to stop timer
[self.autoTimer invalidate];
self.autoTimer = nil;

Retaining object created by a class method?

This is probably something I should know by now, I am creating an instance of NSTimer using the NSTimer class method. I am pretty sure the returned object is autoreleased, my question is in terms of memory management should I be then retaining and releasing the timer object (METHOD: 1), or simply just assigning it directly to the #property (METHOD: 2)(or should I be doing something totally different?)
// METHOD: 1
#property (nonatomic, retain) NSTimer *myTimer;
.
NSTimer *tempTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(executeOnTimer) userInfo:nil repeats:YES];
[self setMyTimer:tempTimer];
//[tempTimer release];
.
- (void)dealloc {
[pulseTimer release];
[super dealloc];
}
OR SIMPLY:
// METHOD: 2
myTimer = [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:#selector(executeOnTimer) userInfo:nil repeats:YES];
EDIT:
One final point, if I just write (see below) without assigning to a property is there any chance that the timer is going to get deallocated, basically does it stay around until the program exits. Just curious how its retained?
[NSTimer scheduledTimerWithTimeInterval:120.0 target:self selector:#selector(executeOnTimer) userInfo:nil repeats:YES];
In order to take ownership over the NSTimer you can do one of these with the same effect:
self.myTimer = [NSTimer scheduledTimerWithTimeInterval: ...]; // implicit setter
or
[self setMyTimer: [NSTimer scheduledTimerWithTimeInterval:...]]; // explicit setter
or
myTimer = [[NSTimer scheduledTimerWithTimeInterval: ...] retain];
or
self->myTimer = [[NSTimer scheduledTimerWithTimeInterval: ...] retain];
This is the good way:
self.myTimer = tempTimer;
// don't call [tempTimer release]
This will retain it automcailcally due to the property which retains it.
Just calling myTimer = … doesn't use the setter while self.myTimer = … does.

NSTimer stopping in run time?

how can i stop NSTimer in runTime?
I am using the following code .but NSTimer runs again and again.(i want to repeat NStimer, i want to stop in runtime )
- (void)TimerCallback
{
.....
[self.tim invalidate];
self.tim = nil;
}
-(void)timerStart
{
self.tim = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(TimerCallback) userInfo:nil repeats:YES];
}
It's repeats:NO if you want it to run only once. You have repeats:YES.
It is [tim invalidate] and not self.tim invalidate
Do not do self.tim = nil, because that is releasing it. invalidate does everything.
For the record, make sure your property is all correct, ie
#property (nonatomic, retain) NSTimer *happyTimer;
and
#synthesize happyTimer;
For the record, you must be on the same thread.
Hope it helps.
Here is all the lines of code cut from a working production example:
NSTimer *ttt;
#property (nonatomic, retain) NSTimer *ttt;
#synthesize ttt;
self.ttt = [NSTimer scheduledTimerWithTimeInterval:7.00
target:self selector:#selector(ringBell) userInfo:nil repeats:YES];
if ( [ttt isValid] )
[ttt invalidate];
[ttt release]; // in dealloc
You need to add some debugging lines NSLog(#"I just invalidated"); and so on, to make sure you don't have some basic mistake.
Your code seems correct. Usually this problem is starting twice the timer. You can try
-(void)timerStart {
[self.tim invalidate];
self.tim = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(TimerCallback) userInfo:nil repeats:YES];
}