Retaining repeating NSTimer for later access? - iphone

I am creating an NSTimer in the createTimer method that I want to refer back to in the later cancelTimer method. To facilitate this I am taking ownership of the NSTimer via a retained property so that I can refer back to it later. The issue that is confusing me is, if I start the timer, cancel it and start it again the code crashes.
#property(nonatomic, retain) NSTimer *walkTimer;
.
-(void)createTimer {
NSTimer *tempTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTimerDisplay) userInfo:nil repeats:YES];
[self setWalkTimer:tempTimer];
}
-(void)cancelTimer {
[walkTimer release];
[[self walkTimer] invalidate];
}
Now I seem to have fixed this by changing cancelTimer to:
-(void)cancelTimer {
[self setWalkTimer:nil];
[[self walkTimer] invalidate];
}
I am just curious why release was not working, my understanding was that:
NSTimer (Autorelease object, not owned by me)
setWalkTimer (takes ownership for me, retainCount+1)
release (relinquishes my ownership, retainCount-1)
invalidate (lets system dispose of timer)
EDIT:
// this fails ...
-(void)cancelTimer {
[[self walkTimer] invalidate];
[walkTimer release];
}
// this works fine ...
-(void)cancelTimer {
[[self walkTimer] invalidate];
[self setWalkTimer: nil];
}
EDIT: 002
Initially I think I was mixing up
#property(nonatomic, retain) NSTimer *walkTimer;
// &
[self setWalkTimer];
and thinking that I needed a release to balance the property, I don't I either overwrite it with a new set (either to another object or nil) and finally release the property in dealloc.
Is the property(retain) the same as retain, I would say no, which is where I was going wrong I think.
EDIT: 003
With regards to this question I think I personally confused things by wrongly using [walkTimer release] As a result the topic drifted to essentially a new question which I have written up as this

You release before you call invalidate. That means by the time you call invalidate, you've already relinquished ownership of the timer. In practice, you end up calling invalidate on a deallocated timer instance.
What you should do is call invalidate before you call release. Since you are using a retained property, you can just set the property to nil:
// Schedule the timer.
self.walkTimer = [NSTimer scheduledTimerWith...];
// Cancel the timer.
[self.walkTimer invalidate];
self.walkTimer = nil;
Update to clear up any confusion regarding memory management
It's important to keep in mind the Memory Management Rules of Objective-C — you own an object if you call alloc, copy or retain on it, and if you own an object, you have to eventually call release. In this case, setWalkTimer: retains the timer because the property is declared as retain — that means you own the timer and must call release on it down the road. The invalidate method does not count as relinquishing ownership of the timer.
When you schedule a timer, the run loop retains it, and when the timer fires or is invalidated, the run loop releases it. But really, you don't need to know that — it's an implementation detail. The call to release by invalidate is only to balance the retain when the timer was scheduled on the run loop.

You need to invalidate before you release. After the timer has fire, you are the only one holding a retain on the timer. So when you call release, the timer deallocates. You then call invalidate on invalid memory and you crash.

Don't retain a scheduled NSTimer if you've set its target to self Don't set self as the target of a repeating timer unless you are absolutely sure to know all the consequences
(...otherwise the runtime drowns a kitten in leaked timers, targets and userInfos — or so goes the saying.)
Please read and re-read "Overview" in the NSTimer Class Reference and pay special attention to the last paragraph.
In a nutshell:
If you schedule an NSTimer, it becomes associated to the current run-loop which retains it.
Furthermore, the timer retains its target.
NSTimer instances are not reusable:
"Once invalidated, timer objects cannot be reused".
So there is no point in retaining a scheduled timer in the first place.
If you need to hang on to it (e.g. in order to cancel it) use a non-owning (aka weak) reference to it.
Update:
For a thorough explanation, see my answer to your other question (it now has graphs — albeit as links only — and stuff).
Please consider the rest of this post (as well as many of my comments) as obsolete.
Your property becomes
#property (nonatomic, assign) NSTimer *walkTimer;
BTW:
-(void)cancelTimer {
[self setWalkTimer:nil]; // great, now [self walkTimer] returns nil so
[[self walkTimer] invalidate]; // here, you are calling [nil invalidate]
}
And since messaging nil is absolutely fine in Objective C, your crash miraculously vanishes...while your timer will happily continue to fire.
Edit
I've forgot to mention:
A timer wants a selector that takes one argument, which will be the timer that fired... Or is that just a typo?

Related

iPhone 4 is there an absolutely certain way to have a long term NSTimer fire

I keep having troubles with my NSTimers and background selectors. It is driving me nuts and takes a very long time to try out each tweak. To preserve my sanity, and the sanity of future generations of cocoa programmers, I'm asking this question:
Is there an absolutely 100% sure way to have a scheduled, long-term timer fire at a later point in time, regardless of whether it was called from a background thread, main thread, etc?
It seems that I keep having to solve the same problem over and over again for the majority of my classes that use NSTimers. they work during short-term testing, let's say I set the timer to fire through a background thread to fire in 10 seconds. It works, because there's still a run loop running. But once I change the fire time to what I really want, like 15-30 minutes, there's dead silence. The run loop is gone and I don't know how to handle such a case. Nothing happens, and I discover such bugs a few days later, once I've already forgotten which timer would be responsible for that.
Currently I'm doing some really, really ugly dance with selectors, for example here's a test method(It seems to work for 10 minute timers):
//this is a test method to simulate a background task requesting a timer
[self performSelectorInBackground:#selector(backgroundReminderLongTermTest:) withObject:nil];
//this is a method similar to the one that the background thread would be trying to invoke
-(void)backgroundReminderLongTermTest:(id)sender
{
[self performSelectorOnMainThread:#selector(backgroundReminderFromMainThread:) withObject:nil waitUntilDone:NO];
}
//this is a wrapper for the background method, I want the timer to be added to a thread with a run loop already established and running
-(void)backgroundReminderFromMainThread:(id)sender
{
[playTimers addObject:[NSTimer scheduledTimerWithTimeInterval:1800 target:self selector:#selector(start:) userInfo:nil repeats:NO]];
}
I like the convenience of not having to worry about creating a fire date object with the scheduled timers, but should I just forget about them and use timers with specific fire dates? It seems that the scheduledTimer works well for short term tasks, when the run loop is already present, but I simply cannot see this kind of bugs during the app's execution. At one point, it seems that the timers are firing normally, but at a later point they stop firing completely.
Thank you for any help or clarification. I'm looking for a method that schedules timers without having to worry about whether or not a run loop is present every time I need to schedule a timer. I want to be sure that as long as the app is running, my timers, scheduled through this method would fire at predictable points in the future .
One of the myriad issues with NSTimers is their run-loop dependency. Every thread has a single run loop. If you schedule a timer on a background thread, it will be scheduled on that thread's run loop. If that thread is short lived, which background threads often are, that timer will quietly die with it.
The solution is to guarantee the timer is run on a thread that will be alive when the timer fires. The best way to do these dedicated background timers in my experience is to not use NSTimer at all, and go for GCD timers instead. Better men than I have coded up GCD powered timers. I personally prefer Mike Ash's article and implementation, which comes with an explanation.
Use local notification instead.
For as long as you depend on using scheduledTimerWithTimeInterval:... you cannot achieve what you want:
The timer will always be tied to the run-loop of the calling thread.
If there is no run-loop associated with that thread by the time of that message's invocation, there surely is one when the method returns as -[NSRunLoop currentRunLoop] creates a run-loop if necessary.
What you can do, if you don't like the other APIs for creation of a timer, is providing a category on NSTimer, which takes care of all the scheduling and so forth and that you can reuse in other projects.
Here is an example of what such a category might look like:
#pragma mark - setting up a timer:
+ (NSTimer *)yourPrefix_mainLoopScheduledTimerWithTimeInterval:(NSTimeInterval)interval target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
{
NSTimer *timer = [self yourPrefix_timerWithTimeInterval:interval target:target selector:selector userInfo:userInfo repeats:shouldRepeat];
void (^scheduler)() = ^{
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
};
if ([NSThread isMainThread]) {
scheduler();
} else {
// you should really be able to rely on the fact, that the timer is ready to roll, when this method returns
dispatch_sync(dispatch_get_main_queue(), scheduler);
}
return timer;
}
// this is just a convenience for the times where you actually want an _unscheduled_ timer
+ (NSTimer *)yourPrefix_timerWithTimeInterval:(NSTimeInterval)interval target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
{
NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:interval];
NSTimer *timer = [[self alloc] initWithFireDate:fireDate interval:interval target:target selector:selector userInfo:userInfo repeats:shouldRepeat];
return [timer autorelease];
}
#pragma mark - tearing it down:
- (void)yourPrefix_invalidateMainLoopTimer
{
[self yourPrefix_invalidateMainLoopTimerAsynchronous:NO];
}
- (void)yourPrefix_invalidateMainLoopTimerAsynchronous:(BOOL)returnsImmediately
{
void (^invalidator)() = ^{
[self invalidate];
};
dispatch_queue_t mainQueue = dispatch_get_main_queue();
if (returnsImmediately) {
dispatch_async(mainQueue, invalidator);
return;
}
if (![NSThread isMainThread]) {
dispatch_sync(mainQueue, invalidator);
return;
}
invalidator();
}
Note the thread checks before using dispatch_sync because...
dispatch_sync
Discussion
[…] Calling this function and targeting the current queue results in deadlock.
(from The GCD Reference — emphasis mine)

Invalidation on auto-invalidation but retained timer

By saying
_requestTimer = [[NSTimer scheduledTimerWithTimeInterval:3.0
target:self
selector:#selector(updateSystems)
userInfo:nil
repeats:NO] retain];
I retain an NSTimer that would, without retain, auto-invalidate (since repeats is set to NO).
I later say
[_requestTimer invalidate];
While this invalidates the timer, as it would do automatically without retain, I keep thinking what happens to the auto-invalidation?
Does [_requestTimer invalidate]; release my retained reference and as well as the reference that would automatically release? Or do I have a memory leak?
According to the profiler, I get a leak, but I don't know if it's my retain, the auto-retain or the profiler not catching up (which would be highly unlikely).
No invalidate does not release you timer, you also do not need to retain your NSTimer if you do not need to deal with it after creation, for example if you do not want to stop it from firing, the NSTimer is retained somewhere within the NSRunLoop which is responsable for fire ring your timer.
Why not just set _requestTimer to nil in your updateSystems method and not do the retain, then by testing _requestTimer == nil you know if your timer has fired (and of course any messages sent to nil are ignored anyway so you win....)

How to work with NSTimer

Im using alot of timers in my application. For recording time, moving object, fading etc. I use the same timer for several puposes in the same view at different times. How should I declare and invalidate or release my timers properly?
Atm Im declaring the timers like this:
fadeTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(bortInfo) userInfo:nil repeats:YES];
and as soon as im not using it im doing this:
[fadeTimer invalidate];
fadeTimer = nil;
The retain count when im leaving the view is 0 on every timer. Should i release the timer in the dealloc aswell? My app runs quite good, but from time to time it crashes.
The clockTimer that i use for updating a label with the time uses
[[NSRunLoop mainRunLoop] addTimer:clockTimer forMode:NSRunLoopCommonModes];
Do i need to do anything with this mainLoop once i invalidate the clockTimer?
All in all please support me with some info about working with timers.
Thank you very much!
Joakim
You're not retaining your timers properly - if you want to refer to them again you should retain them. I'd do this with a property i.e. in your header file
#property (nonatomic, retain) NSTimer *fadeTimer;
and change your code to say
self.fadeTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(bortInfo) userInfo:nil repeats:YES];
// Put this whenever you want to remove your timer and in your dealloc method.
[fadeTimer invalidate];
self.fadeTimer = nil;
This will make sure that your timer is retained by your object. Otherwise you just have to hope that the timer stays around and doesn't get autoreleased by the iPhone. And as you say it's crashing occasionally, this might be the reason ;)
I'm afraid I don't know much about run loop but am confused why your don't just use a normal NSTimer to schedule things - why bother interacting with the run loop at all?
Scheduled timers are retained by the run loop, and retain their target. If you want to retain the timer, you have to jump through a few hoops to prevent a retain cycle (I wrote a non-retaining proxy class, which is a bit messy but it works).
Don't manipulate the run loop unless you know what you're doing (I don't). A "scheduled" timer is already added to the main run loop. If you're generating clockTimer like fadeTimer, then it's being added to the run loop twice.
"from time to time it crashes" doesn't help anyone. Run it in the debugger and see where it crashes. It might even print some messages to the console if you're lucky.
*also you can use and this is a better and optimize way to write this line
if (theTimer != nil) {
if([theTimer isValid]){
[theTimer invalidate];
}
theTimer = nil;
}*

Is there an NSCFTimer memory leak?

I tracked down a memory leak with instruments. I always end up with the information that the responsible library is Foundation. When I track that down in my code, I end up here, but there's nothing wrong with my memory management:
- (void)setupTimer {
// stop timer if still there
[self stopAnimationTimer];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:#selector(step:) userInfo:nil repeats:YES];
self.animationTimer = timer; // retain property, -release in -dealloc method
}
the property animationTimer is retaining the timer. In -dealloc I -release it.
Now that looks like a framework bug? I checked with iPhone OS 3.0 and 3.1, both have that problem every time I use NSTimer like this. Any idea what else could be the problem?
(my memory leak scan interval was 0.1 seconds. but same thing with 5 seconds)
Do not call -[NSTimer dealloc]. Ever.
In this case, -scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: is balanced by -invalidate. You do not need to call -dealloc or -release on the timer object.
Unless your stopAnimationTimer method is invalidate'ing and release'ing (and then setting to nil) your animationTimer property, you're leaking memory.
I found it: I had a strong reference to my timer. The run loop retains it. So RC was 2. But because the Timer also holds a strong reference to the target (which in my case retained the timer), I had a deadlock situation. -dealloc was never ever called, and therefore my timer was never ever freed. WTF.

what is the first step in (NSTimer release and invalidate)?

Can I send argument with #selector in NSTimer? If I want to release NSTimer, are the following steps right in dealloc?
[timer invalidate];
[timer release];
[timer release] only needs to be called if you "own" the timer. From Apple's documentation:
Because the run loop maintains the timer, from the perspective of memory management there's typically no need to keep a reference to a timer once you’ve scheduled it. Since the timer is passed as an argument when you specify its method as a selector, you can invalidate a repeating timer when appropriate within that method. In many situations, however, you also want the option of invalidating the timer—perhaps even before it starts. In this case, you do need to keep a reference to the timer, so that you can send it an invalidate message whenever is appropriate. If you create an unscheduled timer (see “Unscheduled Timers”), then you must maintain a strong reference to the timer (in a reference-counted environment, you retain it) so that it is not deallocated before you use it.
What does this mean?
If you alloc and init a timer, you must also release it, like so:
NSTimer * timer = [[NSTimer alloc] initWith...];
NSRunLoop * runLoop = [NSRunLoop currentRunLoop];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
[timer release];
...
...
[timer invalidate];
timer = nil;
Once the timer has been added to the run loop, there is no reason to keep a reference to it anymore, since the run loops owns it. In this case, as shown, you would release the timer as soon as you add it to the run loop, and then simply invalidate it when you are finished. The final line (setting timer to nil) is for safety. The call to invalidate will result in the timer being released (by the run loop), so it is unsafe to keep a reference that points to it. Setting the local reference to nil keeps things kosher.
If, however, you create a timer using one of the convenience methods like so:
NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval ...];
You do not need to call [timer release] at all! The convenience method adds the timer to the run loop, which then owns it, so you do not need to perform any memory management on the returned timer object. You would simply invalidate the timer when you no longer want to use it:
[timer invalidate];
timer = nil;
Or, if the timer was not set to repeat, you would do absolutely nothing at all, since it would be released after its first invocation.
The two methods do different things. If you own a timer (you retained it, or alloced it, or copied it) then you should release it. If you scheduled it on a run loop, then you must invalidate it for the run loop to release it. If you did both things, then you must release and invalidate the timer (however usually having the run loop owning the timer is sufficient).
Always, release is the last thing you do. Once you release something there is no guarantee it is safe to dereference the object, which means it no longer safe to send it ANY message.
That is the correct way to deallocate a timer that might still be running (and you want to stop).