I have several UIImagesView and I put them on a MutableArray. The MutableArray is retained.
I add them also as a subview of a view.
After adding each UIImageViews on a view I release them... for example...
[myView addSubview:myImageView];
[myImageView release];
If I am not wrong, each imageView has now a retainCount of 2, because they are also stored on the MutableArray.
Now I release the view, so, each imageView retainCount drops to 1.
At some point I do:
myMutableArray = nil;
is it OK to do that? As the imageViews have yet a retainCount of 1, I am not sure if putting the array to nil, the array which were storing the imageViews will release the images... I suppose so, but I would like to hear from you masters!
will this leak?
thanks
You should ignore retain counts because the system adds and removes retain count behind the scenes and you can't possibly track it.
Instead, you need to focus on matching the retains and releases pairs you yourself create.
In this case, the only thing you need to pay attention to is the possible retain of initializing the image views in the first place.
Usually it looks something like this:
UIImageView *imgView=[[UIImageView alloc] initWithImage....
[myMutableArray addObject:imgView];
[myView addSubview:imgView];
[imgView release];
And you're done. The array and view will manage their own retention and you matched the retain of your initialization with a release and all is right with the world.
Yes that will leak! You are setting myMutableArray to nil without releasing the array. Doing so will leak the memory from the NSMutableArray object AND all of the UIImageView objects that it contained. If you want to dispose of the array and its contents, then release the array just like you would release any other object:
[myMutableArray release];
myMutableArray = nil;
The array will then send a release message to each object that it contains.
Related
OK, so I am halfway through developing an iPhone app and I keep stumbling when it comes to memory management.
I have tried a number of times to understand this with very limited success. I consider myself to be above average intelligence, but this stuff just eludes me, despite repeated searches and reading of Apple documentation
Lets say I have a picker that I am creating - so the code goes
UIPickerView *patientPicker = [[[UIPickerView alloc] init]retain];
//more code here
[self.view addSubView:patientPicker];
So then I do a couple of different things with my picker.
The picker only appears when a segmented control button is pressed. The segmented control dictates which array of data is used to populate the picker.
However, when I change segmented control, I find that it displays a new picker on top of the old picker, rather than changes the data in the current picker.
i.e. segmented control is patient age or weight. If age is selected a picker of ages appears, and the same if weight is selected the picker of weight appears. However if one of the pickers is already present, then clicking on the alternate segment doesn't change the data, it just adds another picker onto the view.
My problem comes when I try and hide the picker as the old picker is still underneath, and I can't hide the old one.
So when I click a button to remove the picker, the old picker is still present underneath.
I have tried
[patientPicker removeFromSuperView];
but when I try and rebuild my picker I am advised that patient Picker has been deallocated???
The same goes for
[patientPicker release];
I know that someone would be able to tell me the simple answer, but what I really want is a really simple/dumbed down explanation of memory management so that I don't have to ask again.
Pretend I am 7 years old!
Thanks
Bob
UIPickerView *patientPicker = [[[UIPickerView alloc] init] retain];
Here you don't/shouldn't do a retain. The init call already implies that the caller is responsible for the create object (in other words, init has a retain implied). You need a release for every init and retain.
I reckon that you doing an alloc/init each time the segmented control changes selection. What you can do is, In the vieDidLoad, do this:
UIPickerView *patientPicker = [[UIPickerView alloc] init;
//more code here
[self.view addSubView:patientPicker];
patientPicker.hidden = YES;
[patientPicker release];
When the selection is made on segment control, set the hidden property of the picker to NO and set the datasource according to the selection
this sounds like a job for a tool... yes! hit Build and Analyze and remove every issue =) but understand why the static analyzer flags your program, and understand that there's quite a bit it can catch, yet quite a bit that it can't prove is a reference count imbalance and will not flag these. then run with leaks instrument and fix all issues, etc. and if you run into deallocated instances, run zombies instrument and fix all issues.
anyways, there's more to it! here are some points to your code.
UIPickerView *patientPicker
= [[[UIPickerView alloc] init]retain]; // << do not retain here. alloc
// returns an object you must release
[self.view addSubView:patientPicker]; // << self.view will retain its subviews.
// ok, that makes sense that the view
// would want to hold onto a reference to
// ensure the view is not destroyed
// while it's still a subview.
[patientPicker removeFromSuperView]; << the superview will release its subview
[patientPicker release]; << your life will be easier if you use the accessors
when you're dealing with reference counting, you need to hold a reference to use an object. so, let's take autorelease pools out of the equation. using autorelease only when needed will help you learn, and make some of your issues local to the callsite -- avoid calling autorelease where possible while you are learning.
NSString * a = [[NSMutableString alloc] init]; // << I hold 1 reference
[a length]; // ok
[a retain]; // << I hold 2 references
[a release]; // << I hold 1 reference
[a release]; // << I hold 0 references
[a length]; // expect bad things
now let's illustrate autorelease pools:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSString * a = [[NSMutableString alloc] init]; // << I hold 1 reference
[a length]; // ok
[a retain]; // << I hold 2 references
[a release]; // << I hold 1 reference
[a autorelease]; // << add a to pool. when pool is destroyed a release message will be sent to a
[a length]; // ok. a is still alive. its release message is deferred until the pool is destroyed
[pool release]; // pool's reference count has reached zero. pool will call [a release]. pool will be destroyed.
[a length]; // expect bad things
Two rules of memory management:
If you new, alloc init, retain, or copy (N.A.R.C.) an object, you have to release it.
When the name of a method starts with any of those words, it means it's being created for the caller, who has the responsibility to release the object when he is done with it. Otherwise the method returned is not owned by the caller, and he has to indicate he wants to keep it calling retain on the object.
Note that the first rule results in the second. A method creates an object (therefore it is responsible for releasing it) but if the outcome of the method (the returned object) survives the execution of the method (so it can be handed to the caller) all the method can do is autorelease the object. An autorelease adds the object to the autorelease pool, which will release the object at some point in the future.
Example:
[[MyObject new]; // 1)
[NSString initWithFormat:#"%d",1]; // 2)
[NSString string]; // 3)
1) Name contains new, so it will need release.
2) Name contains init, so it will need release.
3) Name doesn't contain any of the NARC words so you know it returns an autoreleased object. This means you need to retain it if you intend to keep it, and if you do, you will need to release it later. Otherwise just use it and forget about it.
Couple of tips:
Try to retain/release symmetrically so you don't lose track of what to release and where. Example: if you retained on init, then release on dealloc. Same for viewDidLoad/viewDidUnload.
If you can choose, don't abuse autorelease when memory is a concern so you recover your memory as soon as possible. Abusing autorelease is also a sign you don't understand memory management.
Your example (same thing Justin told you):
UIPickerView *patientPicker = [[UIPickerView alloc] init]; // 1)
[self.view addSubView:patientPicker]; // 2)
[patientPicker release]; // 3)
1) We call alloc init so you know this object will need release once you are done with it.
2) addSubView calls retain internally. Why? When you receive an object and you intend to keep it, you express that intention calling retain, which also gives you the responsibility of releasing it. When a UIView is released, its implementation also releases its subviews to balance the retain done before by addSubView.
3) We won't be using it anymore so we call release. Now self.view is the owner of the object.
As an exercise, try to implement your own setter and getter for a variable and run Build and Analyze to see what Xcode thinks about it.
Focus on Ownership, if you have ownership; you have to release object. If you don't have ownership don't dare to release it.
id myObject;
myObject = [abc retain] => you are the owner
myObject = [[abc alloc] init] => you are the owner
myObject = [abc copy] => you are the owner
myObject = [[[abc alloc] init] autorelease] => you are not the owner
(everywhere you put autorelease you loose the ownership)
myObject = [abc xxxWithYYY] => you are not owner
(as a convention, a method returning object always give an autorelease object)
There will be some more similar conventions where you can identify OWNERSHIP; I just jotted down what I can recall now.
A general question: When you add an item to an UIView, does that increase the owner count by 1? Does the main view that you added the item to now becomes an owner as well?
Example:
mainView = [[UIView alloc] init];
UILabel *label = [[UILabel alloc] init];
[mainView addSubview:label] //does this increase owner count by 1?
[label release] //and this decreases it by 1?
You release what you retain/init.
When you call addSubview:, it increases the retain count (or as you say owner count). But that increase belongs to mainView. So it is up to mainView to release the subview some point in the future, not you.
So when you init the label it increases the retain count to 1. When you call addSubview:label it increases the retain count by 1, to 2. Then you release the label, decreasing the retain count back to 1 and counteracting you're previous init.
Then when the label is removed from the mainView its retain count will go back down to 0 and it will be deallocated.
Never use the method retainCount, whether you're just observing it, or acting on it. This method will not display what you expect because of a lot of behind the scenes code. Just don't use retainCount.
Subviews are stored in a NSArray which sends a retain to every object added. In principle, yes the retain count goes as you expect but in reality you can never observe the retain count reliably because of all the retains and releases that occur behind the scenes in the API itself. Trying to track the retain count directly will just lead to grief.
It's better to just follow the rule of if you create an object with new or alloc-init then you release it. If you don't do the former, you don't do the later.
If I create a view, and add it as a subview and also add it to an array, do I have to release it twice?
UIView* cat = [[UIView alloc] initWithFrame:someFrame];
[self.view addSubview:cat];
[self.animals addObject:cat];
[cat release];
[cat release];
It just seems weird to me to have 2 release statements, and I haven't seen people doing that. But doesn't the retain count increase by 2 in this case?
You should only have one release — to balance the alloc. Neither addSubView: nor addObject: give the caller ownership over the object, so the caller does not need to balance them with a release. Reading the memory management guide should clear all this up for you.
It might help to remember "NARC" — if you call a method on an object that includes the word "new", "alloc", "retain" or "copy", you need to release it. As you can see in your code above, only alloc fits the bill. Since you only NARCed it once, you only need to release it once.
In addition to what Chuck wrote:
It's not that you release because addSubview: or addObject: might/might not increase the retain count.
That's completely wrong way to look at it. If self.view retains your object, it's self.view's responsibility to release it correctly, and that's nothing you should care about.
It's because you created the object by alloc you need to release it.
When the cat is added to self.view, then self.view owns the cat, and it is responsible to release the cat. It is the same as for self.animals. The self.animals owns the cat. When self.animals is released, it will release all objects it owned.
So, you should release cat only one. Because in your code block, you own only one cat, not two.
As has been mentioned you do not have to release it twice; once is all that is required. However you do not have to release it at all if you use autorelease:
UIView* cat = [[[UIView alloc] initWithFrame:someFrame] autorelease];
[self.view addSubview:cat];
[self.animals addObject:cat];
The cat object will be released toward the end of the RunLoop cycle. Using autorelease will make your code cleaner and more able to cope with change.
Also you do not need the self qualifiers on view and animals unless you want to distinguish them from locally scoped objects (it'd also be poor practice to have locally scoped variables named the same as your classes' members).
What's the right way to do this?
I have an array that I will use on several methods. I will add objects to it, get values, replace values, etc.
Today I do this:
I declare it on .h, using something like
NSMutableArray *myArray;
as soon as the application starts, I declare it on .m doing something like
myArray = [[[NSArray alloc] init] retain];
If I don't add the retain the array will be released at some point and the application will crash. But allocating the array at the beginning of the application and left it "open" without releasing it will make instruments cry, pointing the finger at me, calling me a "leaker"...
How to solve that? Is this the correct way to do that? how do you guys do stuff like this?
thanks
alloc implicitly sets the retain count to 1. By sending the retain message you're incrementing the retain count to 2. In order for the object to be deallocated you would then need to release it twice. Failure to do so would result in a memory leak.
Ideally you should create the object in your init method using [[NSArray alloc] init] and then release it in your dealloc method like so:
- (void)dealloc {
[myArray release];
[super dealloc];
}
You might also find this article useful: http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
One more thing: You declared myArray as an NSMutableArray but instantiated it as an NSArray. Perhaps that's causing the crash.
You should not retain the object you've just created. You already own it. If, as you say, "the array will be released at some point and the application will crash," that is the code you should change. Your code shouldn't be releasing an object that you still want to keep around.
I've subclassed some UITextField and added some custom properties.
In a UITableViewController, in the ViewDiDLoad I init them, and in the cellForRowAtIndexPath I add them to the cell with [cell.contentView addSubview:customTextField];
Each cell has a different customTextField as all of them are very different.
Where I should call the [customTextField release] ?
After I add them to the cell view ?
If for example I call [self.tableView reloadData] my customTextField are going to be added again to the cell, so maybe I should change my approach in doing this ?
thanks for the orientation ...
regards,
r.
You release an object when you no longer have any interest in it. This happens for many reasons; it might be because you've finished with the object, or have passed the control over the object lifetime to another object. It might be because you're about to replace the object with a fresh instance, it might be because you (the owner object) are about to die.
The last one seems to be relevant in your case. You create these object in viewDidLoad and repeatedly need them (i.e. to add them to cells) until your object is no longer functioning. In this case, just as you create them in viewDidLoad, you can release them in viewDidUnload.
Edit: I should really mention autorelease, but this isn't relevant in this instance. Memory management is best handled with a notion of 'owner' - the person who creates something (or retains it) should be responsible for deleting it (or releaseing in ObjC parlance). autorelease handle some cases where you need to give an object to an alternate owner, having previously owned it yourself (typically via a method return). If you are the creator, you can't just release it before returning it to the new owner, as it will be deleted before the new owner has a chance to stake an interest in it. However, you can't just not release it; it will leak. As such, the system provides a big list of objects that it will release on your behalf at some point in the future. You pass your release responsibility to this list, then return the object to the new owner. This list (the autorelease pool) ensures your release will occur at some point, but gives the new owner a chance to claim the object as theirs before it's released.
In your case, you have a clear interest in owning the objects for the lifetime of your view controller - you need to, at any time, be able to add them to view cells in response to a table data reload. You're only done with them when your view controller dies, so the viewDidUnload (or possibly dealloc) is the only sensible place to release them.
I always release my controls directly after I added them to a view using addSubView. When I work with tables, I also initialize them in the cellForRowAtIndexPath method.
Therefor the object stays alive the shortest time.
Adam Wright explains the theory of this very well, but let me give you some practice. You're thinking about this problem far too hard, and that almost always leads to bugs. There is a simple solution that solves this problem almost every time: Retain all ivars using accessors; don't retain non-ivars.
.h
#interface ... {
UITextField *_customTextField;
}
.m
#property (nonatomic, readwrite, retain) UITextField *customTextField;
...
#synthesize customTextField=_customTextField;
-(void)viewDiDLoad {
self.customTextField = [[[UITextField alloc] init....] autorelease];
}
...
- (void)dealloc {
// I do not recommend accessors in dealloc; but everywhere else I do
[_customTextField release]; _customTextField = nil;
}
Never access your ivars directly, except in dealloc (even that is controversial and some people recommend self.customTextField = nil; in dealloc; there are arguments either way). But never assign your ivars directly. If you will follow this rule, you will find that most of your memory problems go away.
The safest way to handle object ownership is to autorelease the view directly after initialization:
FooTextField* textField = [[[FooTextField alloc] init] autorelease];
[myCell.contentView addSubview:textField];
Adding the text field to a superview (the UITableViewCell's contentView) retains it. This way you don't have to care about releasing the view afterwards.
There seems to be a resentment against autorelease in the iPhone developer community. In my opinion, this resentment is unfounded. Autoreleasing an object adds very little overhead to the program if the objects lives longer than the current pass through the run loop.
Every object in Obj-C has a reference counter (retainCount), and when this counter goes to 0 the object is deallocated. When you allocate and initialize an object, the reference count is set to 1 - but you can retain it as many times you want to.
UITextField *textField = [[UITextField alloc] init]; // Reference counter = 1
[textField retain]; // Reference counter = 2
[textField retain]; // Reference counter = 3
The opposite of retain is release, which subtracts from the reference counter;
...
[textField release]; // Reference counter = 2
[textField release]; // Reference counter = 1
You can always get the reference counter of your objects;
printf("Retain count: %i", [textField retainCount]);
The method addSubview of UIView does retain your passed in sub view - and when it's done with it it releases it. If you need your UITextField later, in another scope (when the UIView is done with it and has released it) - you should not release it after you've added it to the super view. Most of the time you actually don't need to hold on to a reference, so you should release it after you've added it to the super view. If you dont - you can release it in the dealloc method of your scope.
Take a look at UITableView -dequeueReusableCellWithIdentifier: and -initWithStyle:reuseIdentifier:.
In -tableView:cellForRowAtIndexPath:, use -dequeueReusableCellWithIdentifier: and check if the result is nil. If it is, instantiate a new cell with -initWithStyle:reuseIdentifier:.
Send -autorelease to your customTextField upon creation and adding to the respective cell.
You should not add subview in cellForRowAtIndexPath! This will slow down the view as you add a subview each time the cell is displayed. Try using custom UITableViewCell class for that purpose.
Here is a perfect solution of UITableView customization
http://cocoawithlove.com/2009/04/easy-custom-uitableview-drawing.html
works jut perfectly