I am having trouble with memory building up and am not able to empty it once I am done with it. When I look at the diagnostic tool ": Allocations: Instruments: Object summary: statistics", the memory is just continuously building up.
example:
for (int i=0; i<100000; i++){
UILabel *lblPost = [[UILabel alloc] initWithFrame:CGRectMake(x,y,w,d)];
[lblPost setText: "Hello World"];
[self.view addSubview: lblPost];
// tried each of the following
//[lblPost dealloc]; //neither work to clear memory it just builds
//[lblPost release]; //
}
--> Do I need to seperate CGRect out and clear that.
--> (I know I can just keep writing to one label, this is a simplified version where in the bigger version, one label would not work so easily. )
--> (I find it hard to believe that I can not create an object and then destroy it 10000 or 100000000 times over. In standard C, I can accomplish this with memory-blocks by using "free()" )
The view you are adding your label to is retaining it, that's why each none of the labels is deallocated (even if you send release message)
Maybe i really don't understand what you're trying to do, but your each indiviual object you'r allocating is retained in the view. Let me try to explain it in the code:
for (int i=0; i<10000; i++){
UILabel *lblPost = [[UILabel alloc] initWithFrame:CGRectMake(x,y,w,d)];
// lblPost now has a retain count of 1, as you alloc'd it, you'll have to release it!
[lblPost setText: "Hello World"];
[self.view addSubview: lblPost];
// lblPost now has a retain count of 2, as adding to the view adds a reference to it
[lblPost release]
// you alloc'd it, now you should release it. it now has a retain count of 1, which means it's in the ownerhsip of the self.view
}
Now, when you release or free self.view, the lblPost objects should be released as well
Why are you allocating memory for 10000 UILabels and adding them as a subview exactly? That's iPhone torture. The items in bold cause your surge in memory. Plus you're releasing none of them.
Also - never ever ever call dealloc yourself - dealloc is called automatically when you release something.
This is fun! I have decided to jump in. I posted this in my comments but here it is again:
I think madhu misunderstood the [lblPost release]. This "release" only applies to the lblPost instance. Not the ones retained by self.view... etc. So you still have 10000 label retained by self.view...
So, you create 10000 instances of lblPost and then release all of them (10000) by this line [lblPost release] in your for loop. That is just fine. But then in your for loop you also have this line [self.view addSubview: lblPost]. This line will add 10000 instances to your self.view. And they are the reason why your system crashed.
for (int i=0; i<10000; i++){
UILabel *lblPost = [[UILabel alloc] initWithFrame:CGRectMake(x,y,w,d)];
[lblPost setText: "Hello World"];
[self.view addSubview: lblPost];
[lblPost release];
//You should release after adding it to your view
}
Related
Happy November to all,
Well I tried Xcode Build and analyze on my project, and it showed some unusual leaks, which I couldn't quite accept with my knowledge of Objective C.
So I decided to put up a test project and ask here..
MemoryTestController.h
#interface MemoryTestController : UIViewController{
UIImageView *tstImageView;
}
#property(nonatomic,retain) UIImageView *tstImageView;
#end
MemoryTestController.m
#implementation MemoryTestController
#synthesize tstImageView;
- (void)viewDidLoad{
[super viewDidLoad];
self.tstImageView = [[UIImageView alloc] //<==This object is leaking
initWithFrame:<SomeFrame>];
self.tstImageView.image = [UIImage imageNamed:#"SomeImage.png"];
[self.view addSubview:tstImageView];
[tstImageView release];
}
-(void)dealloc{
[tstImageView release];
[super dealloc];
}
#end
When I try Build and analyze, clang static analyzer say
Potential leak of an object at line xx
And the culprit line is
self.tstImageView = [[UIImageView alloc]initWithFrame:<SomeFrame>];
I think I am releasing once for every time I am allocing/retaining. Am I missing something, or Static analyzer has some bugs?
EDIT : Is there any leak there?
Well I run the above project using Leak tool in instrument..It didn't show any leak even though I tried many times..Whom should I believe? Static analyzer or Leak instrument?
your problem is how you release it:
- (void)viewDidLoad{
[super viewDidLoad];
self.tstImageView = [[UIImageView alloc] //<==This object is leaking
initWithFrame:<SomeFrame>];
self.tstImageView.image = [UIImage imageNamed:#"SomeImage.png"];
[self.view addSubview:tstImageView];
[tstImageView release]; // << here
}
you should do it this way:
- (void)viewDidLoad{
[super viewDidLoad];
UIImageView * imageView = [[UIImageView alloc] initWithFrame:<SomeFrame>];
imageView.image = [UIImage imageNamed:#"SomeImage.png"];
self.tstImageView = imageView;
[imageView release];
[self.view addSubview:self.tstImageView];
}
The checker is correct because it cannot assume that the variable is identical to the one you set. Therefore, the form you use in the OP could introduce a reference count imbalance because the ivar's value may not be what you assigned to it by the time you message release upon the ivar.
These cases are not likely for a UIImageView, and quite unlikely in the context of your program, but these examples should give you an idea as to why the checker assumes that object->ivar associations shall not be trusted:
Between creation of the image view and the message to release it via the ivar, you have:
self.tstImageView = [[UIImageView alloc] initWithFrame:<SomeFrame>];
self.tstImageView.image = [UIImage imageNamed:#"SomeImage.png"];
[self.view addSubview:tstImageView];
1) assignment of the image view via the setter
2) access of the image view via the getter
3) direct access of the ivar, when adding to self.view
the setter may have taken a copied or used a cached value. UIImageView is a bad example, but the checker does not know how types are generally passed around - even if it did, it would (at times) make unsafe assumptions.
the simplest example would be:
- (void)setName:(NSString *)inName {
NSString * prev = name;
if (inName == prev) return;
if (0 == [inName count]) name = #"";
else name = [inName copy];
[prev release];
}
the value held by the ivar could change in the meantime. not likely an issue in this case, but let's say that adding the image view as the subview could end up calling back and altering self in the process/effect of adding the subview, and replacing or removing the image view you passed. In that case, the variable view you passed would leak and the view it replaced it with would have a negative imbalance.
Neither of those are likely to happen in your example, but it does happen in real world programs, and the checker is correctly evaluating based on locality, not property (the checker can't assume much of what happens inside a method call). It also encourages one good idiomatic style in this case.
EDIT : Is there any leak there?
Well I run the above project using
Leak tool in instrument..It didn't shown any leak even though I tried
it many times..Whom should I believe? Static analyzer or Leak
instrument?
The static analyzer says there is a potential leak because it is unable to guarantee the reference/allocation it follows is correctly retained/released. You can guarantee that reference counting is correct and please the static analyzer by changing you program to look like I wrote it in my example.
The way you have written it has made it impossible for the analyzer to follow the reference.
If you have no leaks and no zombies, then there is not a leak. But the solution is easy to fix - and programs have a way of changing during development. It's much easier to use the form I posted so it is easier for the toolset and for you to verify the program is correct. The static analyzer is not always correct, but you should adjust your programs to please it because static analysis is very useful. The program I posted is also easier for a human to understand and confirm that it is correct.
when you declare a property with retain like this
#property(nonatomic,retain) UIImageView *tstImageView;
a setter is added that will incr the retainCount when you assign to the property. When you do as below the object you created has already a retainCount == 1
self.tstImageView = [[UIImageView alloc]
initWithFrame:<SomeFrame>];
so the tstImageView object has 2 in retainCount.
do instead
UIImageView* view = [[UIImageView alloc] initWithFrame:<SomeFrame>];
self.tstImageView = view;
[view release];
then, although unrelated to your leak when you release it write like this instead
self.tstImageView = nil;
since the setter will then will properly set the retainCount
What is the best approach to release multiple instances of an object that you can't release right away?
For example, if you are creating a bunch of thumbnails:
for(int i=0; i <totalThumbs; i++){
Thumb *newThumb = [[Thumb alloc] initWithImage:someImage]
//position the thumbs here, etc.
//assume releasing here breaks the app because we need to interact with the thumbs later
// [newThumb release] --- breaks the app
}
Would it make sense to put all the new objects in an array and release them all in viewDidUnload when we no longer need them?
Presumably you are adding each newThumb as a subview of some other view or to an array, so you should be fine to do that and then release newThumb here. For example:
Thumb *newThumb = [[Thumb alloc] initWithImage:someImage];
[myThumbs addObject:newThumb];
[newThumb release];
This works becuase myThumbs retains the object.
In order not to leak the memory, especially if you regenerate the thumbnails, you would want to iterate over the superview's subviews (all the thumbs), remove each from the superview, and release them. You may also need to do this in you dealloc method where you release the superview (assuming you do that). With an array, you could simply call removeAllObjects, I believe.
Maybe I'm missing something, but why not use autoreleasepools?
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for(int i=0; i <totalThumbs; i++){
Thumb *newThumb = [[[Thumb alloc] initWithImage:someImage]autorelease];
}
[pool drain];
Calling autorelease will add it to the pool (that you can create in any scope you like). Just call drain (or release) on the pool when you're done with it. This will release all queued objects.
You can release them right after adding to the array, because the array retains them:
for(int i=0; i <totalThumbs; i++){
Thumb *newThumb = [[Thumb alloc] initWithImage:someImage]
//position the thumbs here, etc.
[thumbsArray addObject:newThumb];
[newThumb release]; // --- doesn't break the app
}
In viewDidUnload and/or dealloc release the array. You don't need to release every single thumb.
We should always avoid allocating memory in a loop. In your case you should release memory immediately after using the object you have created. i.e
for(int i=0; i <totalThumbs; i++){
Thumb *newThumb = [[Thumb alloc] initWithImage:someImage];
//position the thumbs here, etc.
//assume releasing here breaks the app because we need to interact with the thumbs later
// [newThumb release] --- breaks the app
// Work with newThumb
[newThumb release];
}
By doing this the objects get released each time loop runs. Actually each time the loop runs, a new object is created. This is how you can manage memory allocation in a loop.
Cheers!
I have a view controller named SettingsViewController, and a custom UIView subclass named ViewAccounts. In SettingsViewController, I am using the code below to add ViewAccounts view into SettingsViewController.
if(!self.viewAccounts)
{
ViewAccounts *objViewAccounts= [[ViewAccounts alloc] initWithFrame:CGRectMake(0, yViews, 320, 400) withViewController:self];
[self.view insertSubview:objViewAccounts atIndex:0];
self.viewAccounts = objViewAccounts;
[objViewAccounts release];
}
The Problem is that the dealloc method of ViewAccounts (subclass of UIView) is not getting called. If I comment these both lines of code, then dealloc method gets called.
[self.view insertSubview:objViewAccounts atIndex:0];
self.viewAccounts = objViewAccounts;
I guess the problem is with retain count, at it gets incremented by 1 when we use insertSubview or addSubiew, but how to get rid of it. Is this the right approach to use it?
Just as a note: making viewAccount to assign can cause a Zombie down the road (in the original code snippet) because you release it in the last line.
I would suggest the following approach:
Make viewAccounts retain again
Don't use autorelease because you are not returning it from that method
Pay attention to dealloc method of the method in the snippet
This is how I would write the snippet with viewAccounts retaining
if(!self.viewAccounts)
{
ViewAccounts *objViewAccounts= [[ViewAccounts alloc] initWithFrame:CGRectMake(0, yViews, 320, 400) withViewController:self];
[self.view addSubview:objViewAccounts];
self.viewAccounts = objViewAccounts;
[objViewAccounts release];
}
...
- (void) dealloc {
[super dealloc];
[viewAccounts release];
...
}
What happened in your original code was that the retaining variable viewAccounts was not released properly at the end of the this class life cycle and so dealloc was not called.
BTW when you only assign the variable then you have to manage the retain yourself if you re-assign that variable especially if you NIL it. Assume that you set the variable to NIL then the snippet could be executed again. But then you overwrite the variable and you are not able to release the first value. If you use retain the Object-C will do that for you. As a rule of thumb I would always use retain for properties until you have enough experience to handle the other cases.
You could set your objViewAccounts to autorelease, since it will be retained by self.view anyway.
ViewAccounts *objViewAccounts= [[[ViewAccounts alloc] initWithFrame:CGRectMake(0, yViews, 320, 400) withViewController:self] autorelease];
Also regarding the retain count, if the property self.viewAccounts is declared as retain, that would also increase the retain count by 1.
I am hoping to clarify the processes going on here.
I have created a subclass of UIButton whose init method looks like this:
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
self = [UIButton buttonWithType:UIButtonTypeCustom];
[self setTitle:title forState:UIControlStateNormal];
self.frame = btnFrame;
return self;
}
In my view controller I am creating one of these buttons and adding it as a subview:
myButton = [[CustomButton alloc] initWithTitle:#"Title" frame:someFrame];
[self.view addSubview:myButton];
In the view controller's dealloc method I log the retain count of my button:
- (void)dealloc {
NSLog(#"RC: %d", [myButton retainCount]); //RC = 2
[super dealloc];
NSLog(#"RC: %d", [myButton retainCount]); //RC = 1
}
The way I understand it, myButton is not actually retained, even though I invoked it using alloc, because in my subclass I created an autorelease button (using buttonWithType:).
In dealloc, does this mean that, when dealloc is called the superview releases the button and its retain count goes down to 1? The button has not yet been autoreleased?
Or do I need to get that retain count down to zero after calling [super dealloc]?
Cheers.
This deserves two answers.... one for the specific question and one for how memory is managed when the instance is replaced in -init (this one).
Initializers are an odd bird in the Objective-C memory management world. In effect, you are managing self. On entry, self is retained. On exit, you are expected to return either a retained object -- doesn't have to be the same object as self -- or nil.
So, breaking the standard idiom of [[[Foo alloc] init] autorelease] down:
id x = [Foo alloc]; // allocates instance with RC +1
x = [x init]; // RC is preserved, but x may differ
[x autorelease]; // RC -1 (sometime in future)
Note that all retain counts [RC] are expressed as deltas.
Thus, in the init method, you typically don't change the retain count of self at all!
However, if you want to return some other object, you need to release self and retain whatever you are going to return (whether allocated then or previously allocated somewhere else, say when an object is retrieved from a cache).
Specifically, with everything blown out into individual expressions because this answer is being overly pedantic:
- init {
[self release];
self = nil;
id newObject = [SomeClass alloc];
newObject = [newObject init];
if (newObject) {
self = newObject;
... initialize self here, if that is your fancy ...
}
return self;
}
This is more than a little bit tricky. I have summarized my answer in 5 parts:
Creating a custom init method that returns a different object
WARNING: beware of illegal memory access!
How to properly transfer ownership of the button to its parent view
Specific answers to specific questions
A suggestion for improvement
Part 1 : Creating a custom init method that returns a different object:
This is an example of a very special case, namely that the object returned from -initWithTitle:frame: is not the same "object" that was sent the message in the first place.
Normally speaking, the process goes like this:
instance = [Class alloc];
[instance init];
...
[instance release];
Of course, the alloc and init calls are usually grouped together into one line of code. The key thing to notice here is that the "object" (nothing more than an allocated block of memory at this point) which receives the call to init has already been allocated. If you return a different object (as in your example), you are responsible for releasing that original block of memory.
The next step would be to return a new object that has the proper retain count. Since you are using a factory method (+buttonWithType:), the resulting object has been autoreleased, and you must retain it to set the proper retain count.
Edit: A proper -init method should explicitly test to make sure that it is working with a properly initialized object before it does anything else with that object. This test was missing from my answer, but present in bbum's answer.
Here is how your init method should look:
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
[self release]; // discard the original "self"
self = [UIButton buttonWithType:UIButtonTypeCustom];
if (self == nil) { return nil; }
[self retain]; // set the proper retain count
[self setTitle:title forState:UIControlStateNormal];
self.frame = btnFrame;
return self;
}
Part 2: WARNING: beware of illegal memory access!
If you are allocating an instance of CustomButton, and then replacing it with an instance of UIButton, you could easily cause some very subtle memory errors. Let's say CustomButton has some ivars:
#class CustomButton : UIButton
{
int someVar;
int someOtherVar;
}
...
#end;
Now, when you replace the allocated CustomButton with an instance of UIButton in your custom init... method, you are returning a block of memory that is too small to hold a CustomButton, but your code will continue to treat this block of code as if it is a full-sized CustomButton. Uh oh.
For example, the following code is now very, very bad:
- (id)initWithTitle:(NSString *)title frame:(CGRect)btnFrame {
[self release]; // discard the original "self"
self = [UIButton buttonWithType:UIButtonTypeCustom];
[self retain]; // set the proper retain count
someOtherVar = 10; // danger, Will Robinson!
return self;
}
Part 3: How to properly transfer ownership of the button to its parent view:
As for your view controller's dealloc method, you will have to call [myButton release] if you have initialized the button as shown. This is to follow the rule that you must release anything that you alloc, retain or copy. A better way to deal with this issue is to let the controller's view take ownership of that button (which it does automatically when you add the button as a subview):
myButton = [[CustomButton alloc] initWithTitle:#"Title"
frame:someFrame]; // RC = 1
[self.view addSubview:myButton]; // RC = 2
[myButton release]; // RC = 1
Now, you never have to worry about releasing that button again. The view owns it, and will release it when the view itself is deallocated.
Part 4: Specific answers to specific questions:
Q: The way I understand it, myButton is not actually retained, even though I invoked it using alloc, because in my subclass I created an autorelease button (using buttonWithType:).
Correct.
Q: In dealloc, does this mean that, when dealloc is called the superview releases the button and its retain count goes down to 1? The button has not yet been autoreleased?
Also correct.
Q: Or do I need to get that retain count down to zero after calling [super dealloc]?
Sort of :) The retain count may or may not drop down to zero at the point when you log it. Autoreleased objects may still have a retain count of one, since they effectively belong to the autorelease pool for the current run loop. For that matter, the view itself may still belong to a window which has not yet been released. The only thing you really need to worry about is balancing out your own memory management. See Apple's memory management guide for details. From the point of view of your viewController, you have allocated the button once, so you must release it exactly once. When it comes to your custom init... method, things get a little bit trickier, but the principle is the same. A block of memory has been allocated, so it must be released (part 1), and, (part 2) init should return an object with a retain count of one (to be properly released later on).
Part 5: A suggestion for improvement:
You could avoid most of the custom initializer mess by simply creating your own factory method in the same spirit as the one provided by UIButton:
+ (id)buttonWithTitle:(NSString *)title frame:(CGRect)btnFrame {
UIButton * button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setTitle:title forState:UIControlStateNormal];
button.frame = btnFrame;
return button;
}
Note that this approach can still result in memory access errors as identified in part 2
First:
Do not call retainCount
The absolute retain count of an object is next to useless. There are always better ways to reason about memory management in your application.
Next:
Your initWithTitle:frame: method is allocating and returning an instance of UIButton, not an instance of the subclass. If that is what you want, there is no need for a subclass at all.
If you really want an instance of a subclass of UIButton, that is going to be more difficult. A quick google search and a read of the documentation indicates that UIButton really isn't intended to be subclassed.
I just tried:
#interface FooButton:UIButton
#end
#implementation FooButton
#end
FooButton *f = [FooButton buttonWithType: UIButtonTypeDetailDisclosure];
NSLog(#"%#", f);
And it printed:
<UIButton: 0x9d03fa0; frame = (0 0; 29 31); opaque = NO; layer = <CALayer: 0x9d040a0>>
I.e. the sole method to be used to create UIButton instances quite explicitly does not allocate via [self class]. You could go down the path of trying to initialize the instance by hand ala UIView or UIControl, but that is likely a lot of trouble.
What are you trying to do?
Hello Stackoverflow fellow family members!
I've got question regarding memory management in iPhone.
What I did understand was below method
-(void) dealloc
{
// something else to release whatever
// such as Object Created using keyword 'alloc'
// but also Object destroy here its retain value reaches iff 0
// if I do put here NSLog(#"%d", [obj retainCount]); and when it reaches
// not equal to 0 means failure with memory leak.
[super dealloc];
}
So am I understand right? or It is still alight even if retain count reachs > 0 here?
The reason I ask about this question because,
I checked with
NSLog(#"%d", obj.retainCount);
to check the retain count of the object and received value 3. So I tried to release here 3 times to make retainCount here equal to 0, but compiler gives me critical error.
Please, I'm new to the memory de-allocation and retain, release.
Object that I used was 'UIImageView' object and created another instance as,
UIImageView *imageView = //da da~ with UIImage
UIImageView *instance;
// at this point retain count was '1'
instance = imageView;
//[imageView retain];
// at this point retain count was '2'
[self.view addSubView: imageView];
// at this point retain count was '3'
[imageView release];// crashes
// at this point retain count was '2'
but if I do
// but if I add retain on the 'instance = imageView'
// such as
instance = imageView; // then
[imageView retain];
// works but still count is 2...
Thank You.
retainCount is not a reliable debugging-tool:
other objects might still hold references to obj
there are objects that you can't destroy (e.g. string constants)
objects are deallocated if released with a retain count of 1
What you should take care of instead:
balancing references you have with the right amount of release/autorelease
using the Analyzer
using Leaks
The normal process for overriding dealloc is to release any objects that have been previously retained (or alloc'ed) by this instance.
So if somewhere else in the object you have called an alloc or retain method your dealloc would look like:
-(void)someOtherMethod
{
UIImageView *imageView = //da da~ with UIImage
UIImageView *instance;
instance = imageView;
[instance retain];
}
-(void) dealloc
{
//release any retained objects here
[instance release]
[super dealloc];
}
Note that it doesn't matter if the release count hasn't dropped to zero after your particular release, that just means that some other bit of code has also retained the object memory (and that other bit of code will be responsible for releasing it).
Hope this helps.
That's not correct and you should rarely use retainCount. It does not have to be 0 at this point as other objects could have references to the objects you are releasing. What is important to do in dealloc is to release objects that you have ownership on. Which would be objects created with alloc or new etc.