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.
Related
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..
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"
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];
This code works well
#property (nonatomic, retain) NSTimer *timer;
self.timer = [[NSTimer timerWithTimeInterval:kAdsAppearTimeInterval target:self selector:#selector(timerFired:) userInfo:nil repeats:NO] retain];
this code get CFRelease . But why? i use retain property
self.timer = [NSTimer timerWithTimeInterval:kAdsAppearTimeInterval target:self selector:#selector(timerFired:) userInfo:nil repeats:NO];
Not a lot to go on... but:
#property (nonatomic, retain) NSTimer *timer;
self.timer = [[NSTimer timerWithTimeInterval:kAdsAppearTimeInterval target:self selector:#selector(timerFired:) userInfo:nil repeats:NO] retain];
That'll end up retaining the timer 3 times and self once.
Timer +1 for -retain
Timer +1 for scheduling it
Timer +1 for the property assignment
self +1 for being the target of the timer
The timer will be released once when fired (because it'll be unscheduled from the run loop). self will be released when the timer is invalidated or released (you shouldn't have to care).
So, you have two retain counts to account for. The call to retain in the code above is noise; don't bother as the property assignment will retain it.
That leaves the property's retain. The most obvious way is to release the timer in -dealloc.
However, unless you need to potentially invalidate the timer before it fires, there is no reason to have an instance variable referring to the timer at all. Even if you do have an iVar, there is no reason to retain the timer either as long as you set self.timer = nil in your timerFired: method (and set it to nil if you invalidate anywhere).
For a non-repeating timer, if you need a reference to the instance variable, I would not recommend a retain property in its declaration to avoid confusion.
setting the instance variable (myTimer)
myTimer = [NSTimer scheduledTimerWithTimeInterval:myTimerInterval
target:self
selector:#selector(myTimerFired:)
userInfo:nil
repeats:NO];
when the timer fires, you can mark the instance variable as nil since its released when the timer is fired
- (void) myTimerFired: (NSTimer *) theTimer{
myTimer = nil;
//etc
}
This way if you have to reference your instance variable (for example to disable the timer when exiting a View controller)
-(void) onBack {
if(myTimer){
[myTimer invalidate];
myTimer = nil;
}
}
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];
}