Memory leak found, but I can't find it? - iphone

After using Run->Run with Performance tool->Leaks on my application, it finds a leak, but I don't know if it is a glitch by the tool?
I start the application by selecting a button, which opens a tableview (no leaks).
tableview appears and if I don't touch anything (no leaks).
If I select the first cell and cursor appears blinking (3 to 4 seconds later, leak)??
I did find something weird, in my tableview, if I try selecting any other cell, besides the first one, it will not become the first responder (this might not have to do with anything)?
If anyone has come across this or can spot or direct me to where this could be happening please let me know? Thank you.
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
UITextField *FirstField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 130, 25)];
FirstField.delegate = self;
FirstField.tag = indexPath.row; [cell.contentView addSubview:FirstField];
FirstField.returnKeyType = UIReturnKeyNext;
[FirstField release];
return cell;
}

The memory leak is that due to every time you call the function you are allocating a UITextField and adding it to the cell.
Cells are reused and each time the cell is accessed to determine the content a new UITextField is added to the cell and stacked onto of each other
Move the allocation of FirstField to where the cell is allocated so that it can be reused.
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UITextField *FirstField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 130, 25)];
FirstField.tag = 1
[cell.contentView addSubview:FirstField];
[FirstField release];
}
UITextField *field = (UITextField *)[cell.contentView viewWithTag:1];
field.delegate = self;
field.returnKeyType = UIReturnKeyNext;
return cell;
I also see that you are using the field.tag to store the indexPath.row value of the cell, in that cause you might have to search the view hirachy of cell.contentView to find the TextField subview.

Related

Tableview records keep repeating and overlapping

This is my cellForRowAtIndexPath
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(12,11,320,40)];
label.text = [animalArray objectAtIndex:indexPath.row];
[cell addSubview:label];
In the animalArray i have the following items,
Lion, Dog, Cat, Zebra, Donkey, Monkey...
The problem is;
1.) When i add the label inside the if (cell == nil) { condition i see repeated records. like Lion, Dog, Cat, Zebra and then again Lion as i scroll down.
2.) When i add it outside the if (cell == nil) { the records overlap as i scroll.
I understand that this is because the cell is reusing its self, and i am creating a Label each time (so it gets overlapped). I don't know how to fix this issue programatically. Can someone please help me.
EDIT:
I have just displayed 1 label here. but i have more than one label to display. and also some UIComponants.
I writing this code without checking syntax:
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(12,11,320,40)];
label.tag = 556;
[cell.contentView addSubview:label];
}
UILabel *cellLabel = (UILabel *)[cell.contentView viewWithTag:556];
cellLabel.text = [animalArray objectAtIndex:indexPath.row];
you dont have to add label to your table cell. it already has cell.textLabel.text=#"some string".
replace this part:
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(12,11,320,40)];
label.text = [animalArray objectAtIndex:indexPath.row];
[cell addSubview:label];
with this:
cell.textLabel.text=[animalArray objectAtIndex:indexPath.row];
The best approach would be subclassing UITableViewCell and adding all labels. That way you need to set labels' text properties outside of the
if (cell == nil){...}
block.
use this by removing if(cell == nil)
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UILabel *label = [[UILabel alloc]initWithFrame:CGRectMake(12,11,320,40)];
label.text = [animalArray objectAtIndex:indexPath.row];
[cell addSubview:label];
[label release];

How to set the tag correctly of ui element in uitableview- iphone

I have a tableview controller, with a label and a switch displayed in a row. To add elements like this, I am using the approach below(as is recommended to avoid the data in the row getting mixed up).
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
....
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UISwitch* testSwitch = [[UISwitch alloc] initWithFrame:
CGRectMake(200,
0.5 * (cell.frame.size.height-30 ),
30, 30)];
testSwitch.tag = 2;//If this is a dynamic value, it only works for 14 rows
[cell.contentView addSubview:testSwitch];
[testSwitch release];
}
UISwitch *switch1 = (UISwitch*) [cell viewWithTag:2];
//cannot set a dynamic value here
switch1.on = [<array value> boolValue];
I need the tag to be a dynamic value depending on the data on the current row so that I can add an action method when the value changes, and make according model related changes.
However, since the tag is set in the first section, if I make that dynamic, only 14 values(which are the number of rows displayed on the screen) are set.
What do I do so that I can store a dynamic value on all the rows of this switch for its target action method to retrieve?
Thanks..
--Edit---
Changed code to
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
UISwitch* testSwitch = [[UISwitch alloc] initWithFrame:
CGRectMake(200,
0.5 * (cell.frame.size.height-30 ),
30, 30)];
testSwitch.tag = indexPath.row;//If this is a dynamic value, it only works for 14 rows
[cell.contentView addSubview:testSwitch];
[testSwitch release];
}
UISwitch *switch1 = (UISwitch*) [cell viewWithTag:indexPath.row];
switch1.on = someboolValue; //CRASH as switch1 points to UItablviewcell and not UISwitch.
testSwitch.tag=[indexPath row]
assuming you only have one section.
ok, it now sounds like you are having a problem that
[cell viewWithTag:indexPath.row]
is locating the cell and not it's subview so assuming you only have one uiswitch subview:
for (UIView *v in [cell subviews]) {
if ([v isKindOfClass:[UISwitch class]]) {
v.on = someboolValue;
}
}

Leak caught by tools performance

After running the application with (Run performance tool -> leak), the tool found a memory leak.
I run app, view appears and press button for a tableview no leaks.
tableView appears and if I don't touch anything no leaks.
If I select the first cell and leave it alone, 3 to 4 seconds later leak.
Here is my code:
didSelectRowAtIndexPath function is commented out.
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
UITextField *FirstField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 130, 25)];
FirstField.delegate = self;
FirstField.tag = indexPath.row;
[cell.contentView addSubview:FirstField];
FirstField.returnKeyType = UIReturnKeyNext;
[FirstField release];
return cell;
}
You don't need to create the textfield everytime..Since you are setting a tag for the textfield, you try to get the textfield with tag.
UITextField *FirstField = (UITextField *)[cell.contentView viewWithTag:indexPath.row];
if it is nil, then you create the textfield. This may not be a solution for your problem..But it is definitely optimize the memory usage.
Wow I can't believe I didn't see that! #Gomathi doesn't know that that is the memory leak.
Follow, even if the cell already exists you are creating a new UITextField and adding it to the cell. But that does not replace the old TextField because it's not a property it's a subview. You need to set the TextField like so :
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil){
//Configure the cell
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:CellIdentifier] autorelease];
UITextField *firstField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 130, 25)];
firstTextField.tag = 25;// any number will do
firstField.delegate = self;
firstField.returnKeyType = UIReturnKeyNext;
[cell.contentView addSubview:firstField];
[firstField release];
}
UITextField *firstField = (UITextField *)[cell.contentView viewWithTag:25];
firstField.text = #"row specific content";
return cell;
}
+1 Gomathi you had the seed of the answer.

cell with textView

hello every one
i have table view with one cell,and i make on this cell a UITextView that the user can write what he want,this is the code inside the cellForRow:
static NSString *CellIdentifier = #"Cell";
UITextView *text;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
text = [[UITextView alloc]initWithFrame:CGRectMake(1, 5, 200, 140)];
text.tag = 1;
UIView *statusView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 300, 150)];
statusView.tag = 0;
[statusView addSubview:text];
[cell.contentView addSubview:statusView];
}
else {
text = (UITextView *)[[cell.contentView viewWithTag:0] viewWithTag:1];
}
i want to make a save button and i want to know how i can take the text from this UITextView from another method in the class.
thx
Use UIButton to create the button and set a action (method) for that will be called when you click on button.
-(void) buttonPressed:(id) sender
{
//First get the cell (We know we have the only one cell).
// Create an NSIndexPath to access the cell from UITableView.
NSIndexPath *myIndexPath = [NSIndexPath indexPathForRow:0 inSection:0];
//Now We can access the cell using UITableView instance.
UITableViewCell *myCell = [myTabelView cellForRowAtIndexPath:myIndexPath];
//Now access the instance of UITextView using viewWithTag property of UIView.
UITextView* myTextView = (UITextView*) [myCell viewWithTag:1];
//Now you could get the text. :)
NSString* myTextViewText = myTextView.text ;
}

Data in the text field is set to null when I scroll the table view

Hii everyone!
I have some text fields in each cell of the table view, like this
If I enter some data into the text field and scroll the view up and down again, the data in the text field is set to null. Why does this happen, how can I avoid this?
Following is my code for cell construction
static NSString *CellIdentifier = #"Cell";
CustomCellStudentData *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
// cell = [[[CustomCellStudentData alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
cell = [[[CustomCellStudentData alloc] initWithFrame:CGRectZero reuseIdentifier:nil] autorelease];
}
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
switch(indexPath.section)
{
case 0:
switch (indexPath.row)
{
case 0:
{
tfText[0] = [[UITextField alloc] initWithFrame:CGRectMake(125,15, 170, 40)];
cell.accessoryView = tfText[0];
tfText[0].delegate = self;
tfText[0].text = #"";
tfText[0].placeholder = #"<Student Name>";
cell.primaryLabel.text = #"Student Name: ";
}
break;
case 1:
{
tfText[1] = [[UITextField alloc] initWithFrame:CGRectMake(125,15, 170, 40)];
cell.accessoryView = tfText[1];
tfText[1].delegate=self;
tfText[1].placeholder = #"<Student Age>";
cell.primaryLabel.text = #"Student Age: ";
}
break;
}
Thanx in advance
when the cell is scrolled out of window, it may be released, so you must save the input in somewhere and read from there in cell construction method.
Take one cell with one textfield as example:
first I declare a NSString in my interface,and apply to UITextFieldDelegate protocol:
#interface someInterface : UITableViewController
<UITextFieldDelegate>
{
NSString* contentOfTextField;
}
#property(nonatomic,retain) NSString* contentOfTextField;
#end
then, in cellForRowAtIndex:
static NSString *CellIdentifier = #"Cell";
CustomCellStudentData *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
UITextField* field = [[UITextField alloc] initWithFrame:CGRectMake(125,15, 170, 40)];
field.delegate = self;
field.tag = 1;
field.text = self.contentOfTextField;
[cell addSubView:field];
[field release];
return cell;
At last, apply this to update contentOfTextField everytime you edit it:
-(void)textFieldDidEndEditing:(UITextField*)textField
{
UITableViewCell* cell = [self.tableView cellForRowAtIndexPath(/*the indexPath of your cell*/)];
UITextField* t = (UITextField*)[cell viewWithTag:1];
self.contentOfTextField = t.text;
}
Save the data when/while user is writing (set yourself as the delegate of the textfields) and in cellForRow check is there is data saved. If it is, load it. Otherwise set the placeholder.
You had to save the data entered in a table cell, so that when you scroll the tableview , it does not lost.
Hope it helps.....
I also had the same problem.. Then I found problem in
if (cell == nil) {
// cell = [[[CustomCellStudentData alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
cell = [[[CustomCellStudentData alloc] initWithFrame:CGRectZero reuseIdentifier:nil] autorelease];
}
Use
switch(indexPath.section)
and whole code in curly braces...like
if (cell == nil) {
// cell = [[[CustomCellStudentData alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
cell = [[[CustomCellStudentData alloc] initWithFrame:CGRectZero reuseIdentifier:nil] autorelease];
switch(indexPath.section) .........
.........
.......
}
return cell;
}
Hope this will work
First of all, because you explicitly tell it to lose it's data in two places:
tfText[0] = [[UITextField alloc] initWithFrame:CGRectMake(125,15, 170, 40)]; // Number one
cell.accessoryView = tfText[0];
tfText[0].delegate = self;
tfText[0].text = #""; // <== Set this to something useful, instead.
Then there is this:
cell = [[[CustomCellStudentData alloc] initWithFrame:CGRectZero reuseIdentifier:nil] autorelease];
If you want to reuse a cell, it needs that identifier!
As you seem to have at least two types of cells, those should be at least two distinct identifiers...
Next, you are setting yourself as the delegate of the text field — that's all fine and dandy, but do you implement the UITextFieldDelegate protocol and store the data being input in textFieldDidEndEditing:?
There are several ways of solving this, but you should probably create one NSMutableArray per section to store the values from your input fields. (Which you should set up in your initializer.)
Oh and at the moment, you are leaking UITextFields all over the place...