Is there an NSCFTimer memory leak? - iphone

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.

Related

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....)

Retaining repeating NSTimer for later access?

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?

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;
}*

UILabel memory leak?

I have an NSTimer that fires off every second, and on that second I update a UILabel by setting the text property like so:
remainglbl.text = [NSString stringWithFormat:#"%i:%02i", var1, var2];
It works fine, but when I run it in xcode with Start With Performance Tool -> Leaks, it appears that the memory just keeps on climbing and climbing and climbing.
From my understanding, the string should be autoreleased (although I never see the memory decrease, or stop increasing).
Is this a memory leak? Is there a better way I can do this to keep my memory usage in check?
Thanks!
Update: code to create the timer is as follows:
timeTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(changeTime:) userInfo:nil repeats:YES];
code to cleanup at timer finish is as follows:
[timeTimer invalidate];
[timeTimer release];
timeTimer = nil;
Anything wrong with this? I thought the memory might be freed once the timer finishes, but it doesn't.
I believe the problem was that I did not understand the performance tools. Running with the Activity Monitor does not show increasing memory usage.
Just out of curiosity, does the problem still occur if you use [remainingLbl setText: ] instead of setting the property? Your code looks fine... the memory should be cleaned up since stringWithFormat autoreleases the string.
One other thing to try: when you create threads in Objective-C you have to wrap all the code in an NSAutoreleasePool so that the things you create during the thread's execution are cleaned up. That shouldn't make a difference here as one should already exist - but it's worth a shot.

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).