NSTimer not stopping - iphone

I have a Class with a NSTimer *myTimer; variable. At some point I do:
myTimer = [NSTimer scheduledTimerWithTimeInterval:20 target:self selector:#selector(doStuff) userInfo:nil repeats: YES];
further, I have a method:
- (void)doStuff
{
if(myTimer)
{
//do stuff
}
}
and I stop my timer when the class is released through:
- (void)dealloc
{
if (myTimer) { //if myTimer==nil it already has been stopped in the same way
[myTimer invalidate];
myTimer = nil;
}
}
Now, the problem is that when I release the class the timer goes on and on and on firing the event anyway. Am I doing something wrong? It seems the dealloc method is never called, otherwise myTimer would be nil and even if the selector is fired it would not go into the if(myTimer)

This will never work, because timers retain their target, which means your dealloc method will never get invoked until after you've invalidated the timer.
For more info, see the NSTimer documentation and this blog post on "Dangerous Cocoa Calls"

Have you tried the handy debugger tools at your disposal? What happens if you set a breakpoint in your dealloc method? Also, you should post more context around your creation. Is it possible you're creating the timer more than once, thereby replacing the original (but not invalidating it) and leaving it out there to fire at will?

Related

NSTimer invalidate not working

I am trying to create an explosion on the iphone screen which gets bigger fast, then goes away. Why is this timer not stopping?
NSTimer *explosion = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(explosion) userInfo:nil repeats:YES];
-(void)explosion {
image.image = [UIImage imageNamed:#"explosion.png"];
expsize = expsize + 2.5;
image.frame = CGRectMake(image.frame.origin.x, image.frame.origin.y, expsize, expsize);
if (expsize > 60) {
NSLog(#"%f",expsize);
[explosion invalidate];
explosion = nil;
}
}
You are most likely invalidating the wrong timer.
You create a local variable named explosion that has the same name as the instance variable.
Avoid declaring instance variables and local variables with the same name!
I'd suggest that you use the form of selector that the NSTimer doc calls for: - (void)timerFireMethod:(NSTimer*)theTimer. The you can invalidate "theTimer" and be sure you're invalidating the right one.
Also, of course, if "explosion" is declared as a property, then there will be two methods in the class named "explosion", and no real clue as to which one is getting called.
It's hard to be certain, because it's not clear whether this is exactly your code, but you have two variables named explosion, and one of them has an NSTimer assigned to it; the other (which seems to be an ivar) is nil.
// Variable local to whatever method this is in
NSTimer *explosion = [NSTimer scheduledTimerWithTimeInterval:0.1...
if (expsize > 60) {
NSLog(#"%f",expsize);
// Other variable named "explosion" does not exist.
// This is an ivar? Has not been set.
[explosion invalidate];
Assuming you've got explosion declared as a property (and there is no reason not to), you should fix this by using the setter when you create the timer:
[self setExplosion:[NSTimer scheduledTimerWithTimeInterval:...]];
Now the ivar has the timer instance and you can use it to invalidate the timer.
Also, note that your timer's method is incorrect; it must take one parameter which is a pointer to the timer. You can also use this pointer to invalidate the timer when it fires.
- (void) fireExplosion: (NSTimer *)tim {
//...
if( expsize > 60 ){
[tim invalidate];
//...
}
}
Finally, you have one last naming problem; if your property is called explosion, the convention in Cocoa is that the accessor should have the same name, but you have used explosion for the method that your timer calls. This could cause hard-to-track problems later. You should rename the timer method as I have here, using a verb indicating that something is happening.
If you are declaring explosion how you posted in your example then you are shadowing your instance variable explosion. As a word of advice you should use a naming convention for instance variables such as an underscore prefix. Now keeping track of the timer is not required if you only invalidate it after it fires. You could just take an extra parameter on the explosion method that would be the timer explosion:(id)timer. Otherwise you can do the following.
#interface X : NSObject
{
NSTimer *_explosion;
}
#end
And when you go to declare it in your code do the following
...
[_explosion invalidate];
[_explosion release];
//There is a whole 'nother debate on whether or not to retain a scheduled timer
//but I am a stickler for ownership so remember to release this in dealloc
_explosion = [[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(explosion)
userInfo:nil
repeats:YES] retain];
...
-(void)explosion {
image.image = [UIImage imageNamed:#"explosion.png"];
expsize = expsize + 2.5;
image.frame = CGRectMake(image.frame.origin.x, image.frame.origin.y, expsize, expsize);
if (expsize > 60) {
NSLog(#"%f",expsize);
[_explosion invalidate];
[_explosion release];
_explosion = nil;
}
}

NSTimers causing leaks

I've read up a lot about NSTimers, but I must be doing something very wrong with them, because it's practically all the leaks that show up in the Leaks Instrument. The "Responsible Frame" column says -[NSCFTimer or +[NSTimer(NSTimer).
So here's how I have an NSTimer set up in my main menu. I shortened it up to just show how the timer is setup.
.h -
#interface MainMenu : UIView {
NSTimer *timer_porthole;
}
#end
#interface MainMenu ()
-(void) onTimer_porthole:(NSTimer*)timer;
#end
.m -
(in initWithFrame)
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
timer_porthole = [[NSTimer scheduledTimerWithTimeInterval:.05
target:self
selector:#selector(onTimer_porthole:)
userInfo:nil
repeats:YES] retain];
}
return self;
}
When leaving the view, it kills the timers:
-(void) kill_timers{
[timer_porthole invalidate];
timer_porthole=nil;
}
And of course, dealloc:
- (void)dealloc {
[timer_porthole invalidate];
[timer_porthole release];
timer_porthole = nil;
[super dealloc];
}
Don't call retain on your NSTimer!
I know it sounds counter-intuitive but when you create the instance it's automatically registered with the current (probaby main) threads run loop (NSRunLoop). Here's what Apple have to say on the subject...
Timers work in conjunction with run
loops. To use a timer effectively, you
should be aware of how run loops
operate—see NSRunLoop and Threading
Programming Guide. Note in particular
that run loops retain their timers, so
you can release a timer after you have
added it to a run loop.
Once scheduled on a run loop, the
timer fires at the specified interval
until it is invalidated. A
non-repeating timer invalidates itself
immediately after it fires. However,
for a repeating timer, you must
invalidate the timer object yourself
by calling its invalidate method.
Calling this method requests the
removal of the timer from the current
run loop; as a result, you should
always call the invalidate method from
the same thread on which the timer was
installed. Invalidating the timer
immediately disables it so that it no
longer affects the run loop. The run
loop then removes and releases the
timer, either just before the
invalidate method returns or at some
later point. Once invalidated, timer
objects cannot be reused.
Quotes are sourced from Apple's NSTimer class reference.
So your instantiation becomes...
timer_porthole = [NSTimer scheduledTimerWithTimeInterval:.05
target:self
selector:#selector(onTimer_porthole:)
userInfo:nil
repeats:YES];
And now that you're no longer holding the reference to the instance you wont want the release call in your dealloc method.
I've seen you already accepted an answer but there are two things here that I wanted to rectify:
It's not needed to retain a scheduled timer but it doesn't do any harm (as long as you release it when it's no longer needed). The "problematic" part of a timer/target relationship is that...
a timer retains its target. And you've decided to set that target to self.
That means — retained or not — the timer will keep your object alive, as long as the timer is valid.
With that in mind, let's revisit your code from bottom to top:
- (void)dealloc {
[timer_porthole invalidate]; // 1
[timer_porthole release];
timer_porthole = nil; // 2
[super dealloc];
}
1 is pointless:
If timer_porthole was still a valid timer (i.e. scheduled on a runloop) it would retain your object, so this method wouldn't be called in the first place...
2 no point here, either:
This is dealloc! When [super dealloc] returns, the memory that your instance occupied on the heap will be freed. Sure you can nil out your part of the heap before it gets freed. But why bother?
Then there is
-(void) kill_timers{
[timer_porthole invalidate];
timer_porthole=nil; // 3
}
3 given your initializer (and as others have pointed out) you are leaking your timer here; there should be a [timer_porthole release] before this line.
PS:
If you think it all over, you'll see that retaining the timer (at least temporarily) creates a retain-cycle. In this particular case that happens to be a non-issue which is resolved as soon as the timer is invalidated...
You missed [timer_porthole release]; call in your kill_timers method. If you call kill_timers before dealloc method is called, you set timer_porthole to nil, but you did not release it.

How to pause and resume NStimer [duplicate]

This question already has answers here:
Closed 12 years ago.
Possible Duplicate:
How can I programmatically pause an NSTimer?
I have a question. How can I pause a countdown using a timer? I am developing a game. In the game, I need to go to next view when the timer pauses, and after coming back I want to resume it.
I try this code in the view:
[mytimer pause];
// to resume
[mytimer resume];
I try that code, but I get a warning saying: "NSTimer may not respond to 'pause'"
I build with that warning and when I press the pause button, the app crashes.
NSTimer indeed doesn't have resume and pause methods so you can end up with a crash in runtime after such a warning. Generally you can create 2 kinds of timers (see NSTimer class reference) one that implements only once and the second, that repeats. Example:
This way you create a timer, that will enter callback myMethod each second.
NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(myMethod:)
userInfo:nil
repeats:YES];
You probably will choose this one for your purpose where in your class you should maintain some
BOOL pausevariable and in the callback myMethod do the following:
- (void) myMethod:(NSTimer *) aTimer
{
if (!pause) {
// do something
// update your GUI
}
}
where you update pause accordingly somewhere in your code.
To stop the timer (and release it) call
[myTimer invalidate];
good luck
What you want, is what OpenGLES application brings up to you. You should create 2 methods like this:
- (void)startAnimation
{
self.animationTimer = [NSTimer scheduledTimerWithTimeInterval:animationInterval target:self selector:#selector(selector) userInfo:nil repeats:YES];
}
- (void)stopAnimation
{
[animationTimer invalidate];
animationTimer = nil;
}
It's something like this.
Refer to the NSTimer Class Reference, there is no pause method.

Correct way to release NSTimer?

What is the correct way to release a NSTimer in my dealloc method ? It was created with the following code ?
-(void)mainTimerLoop {
mainTimer = [NSTimer scheduledTimerWithTimeInterval:1/10
target:self
selector:#selector(gameLoop)
userInfo:nil
repeats:YES];
}
Thanks
The way you're doing it, you won't ever hit dealloc. A timer retains its target. In this case, that means the timer has retained you. It will not release you until it is invalidated. Since you created the timer, you must also invalidate it at some point prior to dealloc, because the timer's retain will prevent your object's being dealloced.
You have two options:
find another place to invalidate the timer (view goes offscreen, application is terminating, what have you)
set something else as the timer's target.
As an example of the latter:
#interface GameLoopTimerTarget : NSObject {
id owner; /* not retained! */
}
- (id)initWithOwner:(id)owner;
- (void)timerDidFire:(NSTimer *)t;
#end
#implementation GameLoopTimerTarget
- (id)initWithOwner:(id)owner_ {
self = [super init];
if (!self) return nil;
owner = owner_;
return self;
}
- (void)timerDidFire:(NSTimer *)t {
#pragma unused (t)
[owner performSelector:#selector(gameLoop)];
}
#end
/* In your main object… */
/* assume synthesized:
#property (retain, NS_NONATOMIC_IPHONE_ONLY) GameLoopTimer *mainTimerTarget; */
- (void)mainTimerLoop {
self.mainTimerTarget = [[[GameLoopTimerTarget alloc] initWithOwner:self] autorelease];
mainTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0 target:self.mainTimerTarget selector:#selector(timerDidFire:) userInfo:nil repeats:YES];
}
- (void)dealloc {
/* other stuff */
[timer invalidate], timer = nil;
[mainTimerTarget release], mainTimerTarget = nil;
/* more stuff */
[super dealloc];
}
Notice how the time interval is 1.0/10.0 - this could also be written 0.1, but it cannot be written 1/10, as that division will truncate to 0.0.
Also notice how this breaks the retain cycle:
Both you and your timer retain the timer target.
You hit dealloc at the normal time.
You then invalidate the timer and release the timer target.
The timer target is then deallocated.
A valid NSTimer is retained by the run loop, which, if it is repeating, will be forever or until you invalidate it. You shouldn't release it, since, in your example code, you did not explicitly retain it. If you invalidate it, it will no longer be retained by the run loop, and will be autoreleased.
This might be OK for a repeating timer, but is dangerous for a one-shot timer, since it might end being released before you ever access it to see if it's valid and/or try to invalidate it (which would lead to a bad-access app crash). Therefore if you plan on, in any way, looking at a timer variable after it's creation (including to check it, invalidate it and/or release it), it might be a good practice to explicitly retain it somewhere in your app, and then release it and set it to nil after it's invalid and you are done with it.
You can release it and set it to nil in one statement if you declare it as a retain property. Then you can write:
self.timer = nil;
you have a really good answer about NSTimer here How do I use NSTimer? there they talk about stoping a repeating NSTimer doing
[myTimer invalidate];
I think the best advice here is -
Do not retain the NSTimer instance, and do not release it.
As soon as it is scheduled on an NSRunloop (current runloop in the OP's example, an NSTimer is retained by the runloop until being invalidated, or until the runloop stops.
What you should be doing, is to invalidate your timer at the right time - and on the same thread where you created and scheduled it.
Keep in mind, also, that NSTimer retains its target, and won't let the target "die" before it dies itself. design your code so that you don't have a retain cycle that will prevent the releasing of both your object (holding the timer) and the timer (holding you object).
You don't need to release it because it will be autoreleased. Anything created by a convenience method (i.e. you don't call alloc yourself) is the responsibility of the called function to memory manage, which usually means that it will call autorelease on the object it creates before it returns it.
I would assign the timer to a property with the retain keyword though to make sure it doesn't get deallocated on you. Generally autoreleased objects are deallocated in the event loop if they don't have any retains.

Best time to invalidate NSTimer inside UIViewController to avoid retain cycle

Does any one know when is the best time to stop an NSTimer that is held reference inside of a UIViewController to avoid retain cycle between the timer and the controller?
Here is the question in more details: I have an NSTimer inside of a UIViewController.
During ViewDidLoad of the view controller, I start the timer:
statusTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: self selector: #selector(updateStatus) userInfo: nil repeats: YES];
The above causes the timer to hold a reference to the view controller.
Now I want to release my controller (parent controller releases it for example)
the question is: where can I put the call to [statusTimer invalidate] to force the timer to release the reference to the controller?
I tried putting it in ViewDidUnload, but that does not get fired until the view receives a memory warning, so not a good place. I tried dealloc, but dealloc will never get called as long as the timer is alive (chicken & egg problem).
Any good suggestions?
You could avoid the retain cycle to begin with by, e.g., aiming the timer at a StatusUpdate object that holds a non-retained (weak) reference to your controller, or by having a StatusUpdater that is initialized with a pointer your controller, holds a weak reference to that, and sets up the timer for you.
You could have the view stop the timer in -willMoveToWindow: when the target window is nil (which should handle the counterexample to -viewDidDisappear: that you provided) as well as in -viewDidDisappear:. This does mean your view is reaching back into your controller; you could avoid reaching in to grab the timer by just send the controller a -view:willMoveToWindow: message or by posting a notification, if you care.
Presumably, you're the one causing the view to be removed from the window, so you could add a line to stop the timer alongside the line that evicts the view.
You could use a non-repeating timer. It will invalidate as soon as it fires. You can then test in the callback whether a new non-repeating timer should be created, and, if so, create it. The unwanted retain cycle will then only keep the timer and controller pair around till the next fire date. With a 1 second fire date, you wouldn't have much to worry about.
Every suggestion but the first is a way to live with the retain cycle and break it at the appropriate time. The first suggestion actually avoids the retain cycle.
One way around it is to make the NStimer hold a weak reference to your UIViewController. I created a class that holds a weak reference to your object and forwards the calls to that:
#import <Foundation/Foundation.h>
#interface WeakRefClass : NSObject
+ (id) getWeakReferenceOf: (id) source;
- (void)forwardInvocation:(NSInvocation *)anInvocation;
#property(nonatomic,assign) id source;
#end
#implementation WeakRefClass
#synthesize source;
- (id)init{
self = [super init];
// if (self) {
// }
return self;
}
+ (id) getWeakReferenceOf: (id) _source{
WeakRefClass* ref = [[WeakRefClass alloc]init];
ref.source = _source; //hold weak reference to original class
return [ref autorelease];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [[self.source class ] instanceMethodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation invokeWithTarget:self.source ];
}
#end
and you use it like this:
statusTimer = [NSTimer scheduledTimerWithTimeInterval: 1 target: [WeakRefClass getWeakReferenceOf:self] selector: #selector(updateStatus) userInfo: nil repeats: YES];
Your dealloc method gets called (unlike before) and inside it you just call:
[statusTimer invalidate];
You can try with - (void)viewDidDisappear:(BOOL)animated and then you should validate it again in - (void)viewDidAppear:(BOOL)animated
More here
For #available(iOS 10.0, *) you could also use:
Timer.scheduledTimer(
withTimeInterval: 1,
repeats: true,
block: { [weak self] _ in
self?.updateStatus()
}
)
The -viewDidDisappear method may be what you're looking for. It's called whenever the view is hidden or dismissed.
invalidate timer inside - (void)viewWillDisappear:(BOOL)animated did work for me
I wrote a "weak reference" class for exactly this reason. It subclasses NSObject, but forwards all methods that NSObject doesn't support to a target object. The timer retains the weakref, but the weakref doesn't retain its target, so there's no retain cycle.
The target calls [weakref clear] and [timer invalidate] or so in dealloc. Icky, isn't it?
(The next obvious thing is to write your own timer class that handles all of this for you.)
If the timer.REPEAT is set to YES, the owner of the timer (e.g. view controller or view) will not be deallocated until the timer is invalidated.
The solution to this question is to find some trigger point to stop your timer.
For example, I start a timer to play animated GIF images in a view, and the trigger point would be:
when the view is added to the superview, start the timer
when the view is removed from the superview, stop the timer
so I choose the UIView's willMoveToWindow: method as such:
- (void)willMoveToWindow:(UIWindow *)newWindow {
if (self.animatedImages && newWindow) {
_animationTimer = [NSTimer scheduledTimerWithTimeInterval:_animationInterval
target:self selector:#selector(drawAnimationImages)
userInfo:nil repeats:YES];
} else {
[_animationTimer invalidate];
_animationTimer = nil;
}
}
If your timer is owned by a ViewController, maybe viewWillAppear: and viewWillDisappear: are a good place for you to start and stop the timer.
I had exactly the same issue and in the end I decided to override the release method of the View Controller to look for the special case of the retainCount being 2 and my timer running. If the timer wasn't running then this would have caused the release count to drop to zero and then call dealloc.
- (oneway void) release {
// Check for special case where the only retain is from the timer
if (bTimerRunning && [self retainCount] == 2) {
bTimerRunning = NO;
[gameLoopTimer invalidate];
}
[super release];
}
I prefer this approach because it keeps it simple and encapsulated within the one object, i.e., the View Controller and therefore easier to debug. I don't like, however, mucking about with the retain/release chain but I cannot find a way around this.
Hope this helps and if you do find a better approach would love to hear it too.
Dave
EDIT: Should have been -(oneway void)
You can write this code in dealloc function of view controller
for eg.
-(void)dealloc
{
if([statusTimer isValid])
{
[statusTimer inValidate];
[statustimer release];
statusTimer = nil;
}
}
this way the reference counter of statustimer will automatically decrement by 1
& also the data on the allocated memory will also erase
also you can write this code in - (void)viewDidDisappear:(BOOL)animated function