UILabel memory leak? - iphone

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.

Related

Memory management for UIKit in tight loops under ARC

I'm interested in learning more about how best to handle memory management under tight loops with ARC. In particular, I've got an app I'm writing which has a while loop which rus for a really long time, and I've noticed that despite having implemented (what I believe to be) the best practices in ARC, the heap keeps growing boundlessly.
To illustrate the problem I'm having, I first set up the following test to fail on purpose:
while (true) {
NSMutableArray *array = [NSMutableArray arrayWithObject:#"Foo"];
[array addObject:#"bar"]; // do something with it to prevent compiler optimisations from skipping over it entirely
}
Running this code and profiling with the Allocations tool shows that the memory usage just endlessly increases. However, wrapping this in an #autoreleasepool as follows, immediately resolves the issue and keeps the memory usage nice and low:
while (true) {
#autoreleasepool {
NSMutableArray *array = [NSMutableArray arrayWithObject:#"Foo"];
[array addObject:#"bar"];
}
}
Perfect! This all seems to work fine -- and it even works fine (as would be expected) for non-autoreleased instances created using [[... alloc] init]. Everything works fine until I start involving any UIKit classes.
For example, let's create a UIButton and see what happens:
while (true) {
#autoreleasepool {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectZero;
}
}
Now, the memory usage increases ad infinitum -- effectively, it appears that the #autoreleasepool is having no effect.
So the question is why does #autoreleasepool work fine for the NSMutableArray and keep the memory in-check, but when applied to a UIButton the heap continues to grow?
Most importantly, how can I keep the heap from expanding endlessly when using UIKit classes in an endless loop like this, and what does this tell us about the best practices for ARC in while(true) or while(keepRunningForALongTime) style loops?
My gut feeling on this is (and I could be totally wrong) is that it's perhaps something about how the while (true) keeps the runloop from cycling, which is keeping the UIKit instances in memory rather than releasing them... But clearly I'm missing something in my understanding of ARC!
(And to eliminate an obvious cause, NSZombiedEnabled is not enabled.)
As to why the UI* objects grow without bounds? Internal implementation detail. Most likely, some kind of cache or runloop interaction that you are effectively disabling by blocking the main run loop.
Which brings me to the real answer:
Most importantly, how can I keep the heap from expanding endlessly
when using UIKit classes in an endless loop like this, and what does
this tell us about the best practices for ARC in while(true) or
while(keepRunningForALongTime) style loops?
How to fix it? Do not ever use a tight loop on the main thread and Do not ever block the main run loop.
Even if you were to figure out and workaround the UI* induced heap growth, your program still wouldn't work if you were to use a while(...) loop on the main thread. The entire design of iOS applications -- and Cocoa applications -- is that the main thread has a main run loop and that main run loop must be free to run.
If not? Your app will not be responsive (and will eventually be killed by the system) to user input and your drawing code is unlikely to work as expected (since the run loop coalesces dirty regions and draws them on demand in conjunction with the main thread, oft offloading to a secondary thread).
Speculation on my part here, but, it could boil down to the fact that UI-related objects especially tend to use GCD or similar (e.g. performSelectorOnMainThread:…) to ensure some actions happen on the main thread. This is as you suspect - the enqueued block or other unit of execution maintains a reference to the instance, waiting for its time in the runloop to execute, and never getting it.
As a rule it's bad to block the runloop. Once upon a time it used to be relatively common - drag tracking was often done this way (or effectively so, by running the runloop only in a special mode while the drag progressed). But it leads to weird interactions and even deadlocks, because lots of code isn't designed with the possibility in mind - especially in a GCD world where asynchronousity is king.
Remember that you can run the runloop explicitly if you like, inside your while loop, and while that's not quite identical to letting it run naturally, it usually works.
Right, I have done some more thinking about this, together with the great contributions from bbum and Wade Tegaskis regarding blocking the runloop, and realised that the way to mitigate this sort of issue is by letting the runloop cycle, by using the performSelector:withObject:afterDelay: which lets the runloop continue, whilst scheduling the loop to continue itself in the future.
For example, to return to my original example with the UIButton, this should now be rewritten as a method like this:-
- (void)spawnButton {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectZero;
[self performSelector:#selector(spawnButton) withObject:nil afterDelay:0];
}
This way, the method ends immediately and button is correctly released when it goes out of scope, but in the final line, spawnButton instructs the runloop to run spawnButton again in 0 seconds (i.e. as soon as possible), which in turn instructs the runloop to run... etc etc, you get the idea.
All you then need to do is call [self spawnButton] somewhere else in the code to get the cycle going.
This can also be solved similarly using GCD, with the following code which essentially does the same thing:
- (void)spawnButton {
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectZero;
dispatch_async(dispatch_get_main_queue(), ^{
[self spawnButton];
});
}
The only difference here is that the method call is dispatched (asynchronously) onto the main queue (main runloop) using GCD.
Profiling it in Instruments I can now see that although the overall allocation memory is going up, the live memory remains low and static, showing that the runloop is cycling and the old UIButtons are being deallocated.
By thinking about runloops like this, and using performSelector:withObject:afterDelay: or GCD, there are actually a number of other instances (not just with UIKit) where this sort of approach can prevent unintentional "memory leaks" caused by runloop lockups (I use that in quotations because in this case I was being a UIKit rogue.. but there are other cases where this technique is useful.)

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

Autorelease vs. Release

Given the two scenarios, which code is best practice and why?
Autorelease
loginButton = [[[UIBarButtonItem alloc] initWithTitle:#"Login"
style:UIBarButtonItemStylePlain
target:self
action:#selector(loginButtonClicked:)]
autorelease];
self.navigationItem.rightBarButtonItem = loginButton;
or
Release
loginButton = [[UIBarButtonItem alloc] initWithTitle:#"Login"
style:UIBarButtonItemStylePlain
target:self
action:#selector(loginButtonClicked:)];
self.navigationItem.rightBarButtonItem = loginButton;
[loginButton release];
For your example, it doesn't really matter. Personally, I would probably use the first case. That would let you add modifications or debugging code later without having to worry about moving the [loginButton release] line around.
There seems to be a stigma against using autorelease (i.e. prefer to release whenever possible), which is why I typically go the second route. But since you're not in a loop here, releasing now vs. autoreleasing later will have exactly the same effect (since another object has retained loginButton, it won't be dealloc()ed).
But I should point out that most of my memory leaks are caused by forgetting to add the release line, so it would probably be better to just tack on the autorelease right away.
Either will be just fine in your case, as Carl says. It is because the UIBarButtunItem object stays in the memory because one reference to it is kept inside self.navigationItem (assuming you declared that property with #property (retain).). Therefore, the usual diatribe against using the autorelease pool, that it keeps unnecessary objects in memory until the end of the current event loop, doesn't apply here.
Since you're on a very tight memory budget on the iPhone, the preferred way should be through explicit release. This way objects don't stick around until the autorelease pool gets emptied during the runloop and you are able to keep your memory footprint as small as possible.
Since the navigationItem retains it, the two end up being identical. Style-wise, autorelease is preferred for returns from methods that don't say alloc or copy in their name, but otherwise it is up to you. If the object wasn't separately retained, release would free up the memory faster.
The code-style issue of setting a reference to nil after release is a related question.
When you send an -autorelease message to an object, you're adding it to a list, and it will get a -release message when the autorelease pool is released. The entire purpose of -autorelease is to provide a way for you to balance your retains and releases when something else might still want the object you're releasing, but you don't. In the situation you describe, the second example you gave is better.
Though in the presented scenario the two cases are identical but there is a slight speed advantage to using release (reference )over autorelease.So when there are strict performance requirements go with release

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.

iPhone - array lost between appDidBecomeActive: and run loop starting?

EDIT: I stuck in a single call to manually retain the array right after initializing it, and that fixed it. Dur. Thanks.
I keep an NSMutableArray in a UIScrollView subclass, and I add to it instances of a UIImageView subclass as each is added as a subview. It needs to be a mutable array, as I plan to rearrange their order in the array eventually; this bug is occurring before I even try to do so. I've used NSLogs to show that the array has count 10 up until and including applicationDidBecomeActive: is called in my AppDelegate, and added a timer to fire every 0.001s in the run loop. The first time it fires, it can't find the array anymore! I'm very confused and have been trying to track this down for hours.
The timer's working properly (i.e. when printCount: doesn't access the array, it's fine), it's only when I try to access the array there that it breaks. The regular call to printCount: below works, but the first time the timer fires I What could possibly be happening between these two calls to printCount:?
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSTimer *timer = [[NSTimer scheduledTimerWithTimeInterval:0.001f
target:[viewController view]
selector:#selector(printCount)
userInfo:nil
repeats:YES]
retain];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
[[viewController view] printCount];
}
I've been endlessly stepping through the program with the debugger. The array is correct right up through this point, and its memory address is the same even after its contents are changed. However, there is only object in it afterwards -- its 10 elements get replaced by a single UITouchData instance. No idea where it comes from. None of my event-handling methods attempt to reassign the array or change it. Any ideas what could be going wrong? I'm completely mystified.
Did you -retain the array?
Perhaps your object was released? What happens if you retain it. Does that work or you just get a memory leak?
its 10 elements get replaced by a
single UITouchData instance. No idea
where it comes from.
Is it always a UITouchData instance? Always? -- Cause that would seem like a dead give-away (if its not the SAME every-time) of a memory problem.
I stuck in a call to retain to array right after initializing it, and that fixed it. Dur.
Thank you for the suggestions. As usually happens with problems like these, it was very simple and finally giving in and asking for help triggers a solution and makes me feel stupid. :D
Does the view initialize and release the array?
Are you sure that the NSView isn't released in between?
You could check if the memory address of [viewController view] (== self within printCount) is the same every time your timer calls printCount.