views:

86

answers:

7

Hello,

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.

+1  A: 

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.

Wim Haanstra
In my personal case, my customTextField must exist to all UITableViewController objects, no matter if they are visible or not becasue the row position, so I have to initialize in the very first oportunity. Maybe I should call the [customTextField relase] in the - (void)dealloc ?
mongeta
+1  A: 

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.

Adam Wright
Always release your objects when you're about to die!
Douwe Maan
It's strange but the viewDidUnload isn't firing when I go to the next UIViewController in that case, I'm going to investigate this first
mongeta
Define "Go to the next UIViewController". If you're just pushing one onto a navigation view stack, then the view doesn't unload (it does however, disappear via `viewDidDisappear`).
Adam Wright
A: 

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.

Nikolai Ruhe
And this work also for objects that are in the .h file ? UISegmentedControl *segmentedControl; ... @property (nonatomic, retain) UISegmentedControl *segmentedControl; segmentedControl = [[UISegmentedControl alloc] initWithItems:itemArray]; => works segmentedControl = [[[UISegmentedControl alloc] initWithItems:itemArray] autorelease]; => doesn't work
mongeta
You appear to be using `autorelease` as a "poor man's garbage collection". That is not its purpose. You are subverting the intent, which may lead to maintenance issues. It may also obscure subtle memory management bugs in some cases. I think its a dangerous habit to get into, "autorelease so we don't have to care about it". It doesn't really save you anything. Instead of writing `[... autorelease]` in one place you'll write `[object release]` in another - usually just after the point of transfer of ownership, end of scope, or in a release or unload method.
Phil Nash
@mongeta: When setting instance variables directly (not using their accessor methods) you have to take care for correct object ownership, of course.
Nikolai Ruhe
@Phil Nash: I disagree. Autoreleasing objects after allocating them has nothing to do with garbage collection. It's just safer to take care of object ownership at only one place: allocation. This way no bugs are introduced when your method returns prematurely or an exception occurs before you manually release the object. Maintenance and readability suffer when releasing allocated objects after being done with them.
Nikolai Ruhe
A: 

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

Valentyn
I've used in other places custom UITableViewCell, but when all of them share the same object, but here, I need a different object in each cell. What I'm thinking now is creating the cells first in the viewDidLoad and then just assign it them cellForRowAtIndexPath.
mongeta
Thanks to all for the great answers, still don't know where to give my vote ...
mongeta
A: 

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.

Björn
+1  A: 

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.

Rob Napier
A: 

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.

Hwee-Boon Yar