Sending messages to released objects? - iphone

I have several UIView subclasses (buttons, labels, etc.) that follow the following setup pattern. My question is, why are messages still able to be sent to the UILabel after release?
myLabel = [[UILabel alloc] initWithFrame:someFrame];
[someUIView addSubview:myLabel];
[myLabel release];
myLabel.textAlignment = UITextAlignmentCenter;
// other property changes to myLabel
They are "owned" by a new UIView, I suppose, but I don't understand why release doesn't destroy the original object and thereby all messages to it. I'm not making property changes through someUIView's subViews. I'm not complaining. I'm just trying to understand why.
EDIT: I should add that these are instance variables, if that makes a difference.

The object is not destroyed as long as the retain count is greater than 0. In this case someUIView has retained the object.
It is really best not to access an object after releasing it. a better pattern might be:
myLabel = [[[UILabel alloc] initWithFrame:someFrame] autorelease];
myLabel.textAlignment = UITextAlignmentCenter;
[someUIView addSubview:myLabel];
myLabel = nil;
Second example:
myLabel = [[UILabel alloc] initWithFrame:someFrame];
[someUIView addSubview:myLabel];
myLabel.textAlignment = UITextAlignmentCenter;
// other property changes to myLabel
[myLabel release];
myLabel = nil;

Your call to -addSubview: calls -retain on the label when it receives it. At this point, you relinquish ownership (by calling -release) and only the view owns it. But it still exists until the containing view also releases it.

You can still send messages to the label because the label hasn't been released yet. -addSubview: retains the objects passed in, so the object remains in memory since the view is still holding a reference and you didn't nil the myLabel pointer.

Because they are probably retained before...

Related

iPhone: I am having trouble with memory iteratively building up

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
}

Object leak using "retain"

I have a property defined with retain attribute which I am synthesizing:
#property (nonatomic, retain) UISwitch *mySwitch;
And inside my loadView I am doing this:
self.mySwitch = [[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 40, 20)];
And finally inside my dealloc I am doing this:
self.mySwitch = nil;
Am I leaking this object (mySwitch) as I have used one alloc? Should I autorelease it while assigning it frame?
Please suggest.
The line:
self.mySwitch = [[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 40, 20)];
Actually calls retain twice- once for the alloc and again in the assignment to self.mySwitch (which is a property you've specified should retain any values assigned to it.) The fix I have been told is best is to add a call to autorelease on the line, making it:
self.mySwitch = [[[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 40, 20)] autorelease];
Yes, you are leaking. You are creating an owned object with +alloc/-initWithFrame:, then assigning that owned object to a property marked retain. This creates a second owned reference to the object. At this point, you leak your original owned reference, which causes the object itself to leak.
The correct behavior here is to call -autorelease on the object before assigning it to the property.
self.mySwitch = [[[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 40, 20)] autorelease];
On a tangential note, it's not recommended that you access properties inside of -dealloc. The two reasons generally given for this are 1) this will broadcast KVO notifications, which you don't want inside of -dealloc, and 2) if anyone overrides the setter (in this class or a subclass) it may not behave properly. The recommended approach is to simply release the underlying ivar, so you'd see something like the following instead:
[mySwitch release];
Assigning nil to the property is perfectly safe (and recommended) everywhere else.
As alternative to autorelease, if you need a tighter memory management this should work for you:
UISwitch *myswitch_tmp= [[UISwitch alloc] initWithFrame:CGRectMake(0, 0, 40, 20)];
self.mySwitch = myswitch_tmp;
[myswitch_tmp release];
and later e.g. in dealloc
[mySwitch release];
Yes. You are leaking object. Remember one simple rule here:
if you used +alloc there is always must be corresponding -release.

(iphone) basic memory management question

I have used following 2 patterns to create a view.
#property (retain, nonatomic) SomeView* someView;
...
// First pattern
self.someView = [[SomeView alloc] initWithFrame frame];
// Second pattern
SomeView* aSomeView = [[SomeView alloc] initWithFrame];
self.someView = aSomeView;
[aSomeView release];
Now, looking back at this code, the first pattern's method should be changed to
self.someView = [[[SomeView alloc] initWithFrame frame] autorelease];
shouldn't it?
I feel dumb :(
Look at it like this:
[[SomeView alloc] initWithFrame: frame];
The above line creates an object and gives it a retain count of 1.
self.someView = [[SomeView alloc] initWithFrame: frame];
This line leaves it with a retain count of two, because the someView property is declared with retain:
#property (**retain**, nonatomic) SomeView* someView;
So, doing it this way leaves your someView property pointing to an object with retain count of 2. You can do it this way if you add an autorelease call to it:
self.someView = [[[SomeView alloc] initWithFrame: frame] autorelease];
Your second pattern is better, if you ask me. You create an object with a retain count of one. You assign it to a retaining property (now it has a retain count of 2) and then you release the original variable, leaving the object again with a retain count of 1. It's three lines where you might want only one, but it makes sense in the right context. Additionally, it's usually best to avoid using autorelease outside of an alloc or copy method since its usually an indication you don't fully understand memory management in Obj-C.
And as a commenter said in the comments to the question, don't feel dumb. None of this is intuitive at first. Nobody picks up a guitar and plays like Hendrix their first time.
Yes, you are right. autorelease means "release a bit later".
Yes, I think you should change that. With self.someView = you are calling the setter which increases the retain count.
Now, looking back at this code, 1's method should be changed to self.someView = [[[SomeView alloc] initWithFrame frame] autorelease];
shouldn't it?
correct
a)
SomeView * view = [[SomeView alloc] initWithFrame:frame];
self.someView = view;
[view release], view = nil;
b)
self.someView = [[[SomeView alloc] initWithFrame:frame] autorelease];
many people prefer b, simply because it is less to type.
i prefer an approach similar to a because:
defects (such as over-releasing) are often exposed near the call site, rather than when the pool is destroyed (this often means you have to load up Instruments in Zombie mode to locate the callsite)
it performs better and minimizes memory usage (in general, but not much in this specific case)
you have more opportunity to check for invalid states and results
you have a chance to init/configure the view/object for its usage before adding it to self, which is usually preferred

How to reuse a UILabel? (Or any object)

This:
UILable *myLabel = [[UILabel alloc] init];
UILable *myLabel = [[UILabel alloc] init];
gives me a redefinition error.
But this:
for(i=0;i<5;i++)
{
UILable *myLabel = [[UILabel alloc] init];
// some label code here
[self.view addSubview:myLabel];
[myLabel release];
}
doesn't. So is the second one false? Should I define it before and just reuse it?
Is that right:
UIIMageView *Sign;
//Some Sign Stuff
Sign = [[UIImageView alloc]init];
Sign.image = [UIImage imageNamed:#"Minus.png"];
frame = CGRectMake(160 ,80, 64, 64);
Sign.frame = frame;
[scrollView addSubview:Sign];
Sign = nil;
[Sign release];
//Some other Sign stuff
Sign = [[UIImageView alloc]init];
Sign.image = [UIImage imageNamed:#"Plus.png"];
frame = CGRectMake(200 ,80, 64, 64);
Sign.frame = frame;
[scrollView addSubview:Sign];
Sign = nil;
[Sign release];
is that correct? That doesnt work without the Sign = nil. So it seems a little wobbly too.
You cannot have identical variable names used in the same block level scope. So in your first example you cannot have a variable definition with the same name, you have to name them differently.
- (void) method {
UIImageView* image1;
// here we define a new block scope. This can be a block of any kind (while, for, if)
{
// All reference in this block to this variable will see this definition.
UIImageView* image1;
// Using image1 here
}
// Here we see again the image1 defined at the beginning of the method.
}
In your loop example you are in a new scope that it's reinitialize after each iteration.
Your third example is correct in that it define the variable only one time. You reuse this variable after that to assign a new object. The third one is less elegant in that your variable name does not describe well for each case what are their purpose.
For your case of 'Sign = nil' this effectively make the line that follows useless since in Objective-C a message sent to a nil object is ignored.
I would suggest to define a method that you can call to create your images that look the same. Something like:
- (void) createImage:(NSString*) image frame:(CGRect) frame {
UIImageView *Sign;
Sign = [[UIImageView alloc]init];
Sign.image = [UIImage imageNamed:image];
Sign.frame = frame;
[self.scrollView addSubview:Sign];
[Sign release];
}
Your for-loop is perfectly fine. The scope of myLabel is limited to one run of your for-loop. So each run a new variable to hold the reference to your UILabel is created.
The second code you posted has leaks.
Sign = nil
[Sign release]
This will release the object at address nil and not the object you created. I can't see what else is wrong with your code, but your fix is definitely not fixing the root cause. Maybe it will help to post what error/warning you get when removing Sign = nil.
Also note that starting your variable names with a capital letter is not a good naming convention, because usually class names start with one.

IB objects vs manually allocated objects in init/viewDidLoad

When I programmatically allocated a UILabel in my custom initWithNibName method, and later in viewDidLoad, tried to assign a string to it, the label was not pointing to anything. I didn't release it; the label shows on the screen. If I create the label in IB, and assign text to it in viewDidLoad, it works.
Is it against a rule to set up manually allocated objects in viewDidLoad?
Why is it not pointing to anything, even though viewDidLoad is called after my init?
From the doc of viewDidLoad:
This method is called after the view controller has loaded its associated views into memory. This method is called regardless of whether the views were stored in a nib file or created programmatically in the loadView method. This method is most commonly used to perform additional initialization steps on views that are loaded from nib files.
In my init:
_descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 218, 280, 10)];
_descriptionLabel.numberOfLines = 0;
_descriptionLabel.lineBreakMode = UILineBreakModeWordWrap;
_descriptionLabel.font = [UIFont systemFontOfSize:12.0];
_descriptionLabel.adjustsFontSizeToFitWidth = NO;
_descriptionLabel.text = #"Description not found.";
_descriptionLabel.backgroundColor = [UIColor clearColor];
In viewDidLoad, the variable's value is 0x0.
It's the same with my manually allocated UIButton, which is fully working once the view loads.
If you want to create the UILabel programatically you can, but you still do it in viewDidLoad (as opposed to initWithNibName).
Don't be afraid to do UI setup in viewDidLoad. It is provided to add any static UI elements BEFORE the view appears on screen.
The view will not appear until just before viewDidAppear:(BOOL)animated is called.
If you have dynamic content configure it in viewWillAppear:(BOOL)animated. (This looks like your situation)
Then make sure you add it to the view:
[self.view addSubview:myLabel];
If you need future access to your new label, you will need to create an ivar to hold a pointer to it.
In the code posted, the _descriptionLabel UILabel is not retained and will be released before the view is drawn.
Try
_descriptionLabel = [[[UILabel alloc] initWithFrame:CGRectMake(20, 218, 280, 10)] retain];
Make sure you put [_descriptionLabel release] in dealloc. This assumes _descriptionLabel is an instance variable.
This basically applies to any object you create with alloc, copy, or new.