简体   繁体   中英

UITableView cell memory leak

I am having a leak in my tableview that I am populating with data.

Here is what is happening:

I have an NSMutableArray filled with NSDictionaries (contact information) that I load from sql database each time the user would select a new index letter, the array is filled with info for those contacts.

Since I am using an instance variable array to hold the data I figure something like this before each time new data is swapped in:

if (currentConnections != nil) {
    [currentConnections release];
    currentConnections = nil;
}

Would effectively get rid of my old data before I reset it.
However, this is where the strangeness begins. In my tableView:cellForRowAtIndexPath: I make calls like this:

NSString *firstname = [[currentConnections objectAtIndex:indexPath.row]
                       objectForKey:@"firstname"];
UILabel *name = [[UILabel alloc]
                 initWithFrame:CGRectMake(75, 12, 190, 15)];
name.font = [UIFont fontWithName:@"Gotham-Medium.ttf" 
                            size:12];
name.textColor = [UIColor whiteColor];
name.backgroundColor = [UIColor clearColor];
name.text = firstname;

Where current connections is the array of dictionaries. I then add the UILabel as a subview of the cell:

[cell addSubview:name];
[name release];

The behavior from this is such:

When I run as is (releasing current connections before each swap then loading the new data to it) the app will crash for a bad access error.
When I do not release the subviews I add to the cell the app does not crash but I watch in horror as live bytes continue to accumulate in the memory allocations tool until the point when I switch contact info enough that I get a memory warning then crash.

It seems to me that there is some weird memory ownership issues going on with the cell and subviews. I do specify the cell as reusable and assign it to autorelease before loading each with data.

Any ideas of what may be causing this leak?

edit:

This is the current tableView:cellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
 {

static NSString *MyIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];

cell.accessoryType = UITableViewCellAccessoryNone;

if (cell == nil) {
    NSLog(@"within");
    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}

if (!ALOCATED) {


cellName = [[ UILabel alloc ]
                 initWithFrame:CGRectMake(75, 12, 190, 15)
                 ];
cellName.font = [ UIFont 
             fontWithName:@"Gotham-Medium.ttf" 
             size:12 
             ];
cellName.textColor = [ UIColor whiteColor ];
cellName.backgroundColor = [ UIColor clearColor ];

NSString *firstname = [[ currentConnections 
                        objectAtIndex:indexPath.row 
                        ] objectForKey:@"firstname" 
                       ];

firstname = [ firstname stringByAppendingString:@" " ];

NSString *lastname = [[ currentConnections 
                       objectAtIndex:indexPath.row 
                       ] objectForKey:@"lastname" 
                      ];

UIImage *profPic = [[ currentConnections 
                     objectAtIndex:indexPath.row 
                     ] objectForKey:@"profilepicture" 
                    ];
connectionPhoto = [[ UIImageView alloc ] initWithFrame:CGRectMake(10, 8, 56, 56) ];
connectionPhoto.image = profPic;




NSString *fullname = [ firstname stringByAppendingString:lastname ];

cellName.text = fullname;


[ cell addSubview:cellName ];



cellTitle = [[ UILabel alloc ]
                 initWithFrame:CGRectMake(75, 31, 260, 13)
                 ];
cellTitle.font = [ UIFont 
             fontWithName:@"ArialMT" 
             size:10 
             ];
cellTitle.textColor = [ UIColor whiteColor ];
cellTitle.backgroundColor = [ UIColor clearColor ];
cellTitle.text = [[ currentConnections 
                  objectAtIndex:indexPath.row 
                  ] objectForKey:@"title" 
                 ];


[ cell addSubview:cellTitle ];
[ cell addSubview:connectionPhoto ];
[ profPic release ];

    ALOCATED = YES;

} else {



    cellTitle.text = [[ currentConnections 
                       objectAtIndex:indexPath.row 
                       ] objectForKey:@"title" 
                      ];

    connectionPhoto.image = [[ currentConnections 
                                                             objectAtIndex:indexPath.row 
                                                             ] objectForKey:@"profilepicture" 
                             ];


    NSString *firstname = [[ currentConnections 
                            objectAtIndex:indexPath.row 
                            ] objectForKey:@"firstname" 
                           ];

    firstname = [ firstname stringByAppendingString:@" " ];

    NSString *lastname = [[ currentConnections 
                           objectAtIndex:indexPath.row 
                           ] objectForKey:@"lastname" 
                          ];
    NSString *fullname = [ firstname stringByAppendingString:lastname ];

    cellName.text = fullname;

}

return cell;

}

Where I am attempting to create one cell and reuse it, only changing the properties of its subviews each time.

EDIT

What was happening was that I was adding subviews to the cell object itself, which was causing a leak as these were not being deallocated. As soon as I started adding the views to the UITableViewCell member views aka cell.contentView, cell.accessoryView etc... the memory was released. So as daniel mentioned below, either create a custom table cell and add your views to your cell properties or add your views to the member uitablecellviews not the cell itself. The following worked for me:

   - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{

static NSString *MyIdentifier = @"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];

cell.accessoryType = UITableViewCellAccessoryNone;

if (cell == nil) {

    cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier] autorelease];
}




UILabel *cName = [[ UILabel alloc ]
                 initWithFrame:CGRectMake(105, 10, 190, 15)
                 ];
cName.font = [ UIFont 
             fontWithName:@"Gotham-Medium.ttf" 
             size:12 
             ];
cName.textColor = [ UIColor whiteColor ];
cName.backgroundColor = [ UIColor clearColor ];

NSString *firstname = [[ currentConnections 
                        objectAtIndex:indexPath.row 
                        ] objectForKey:@"firstname" 
                       ];

firstname = [ firstname stringByAppendingString:@" " ];

NSString *lastname = [[ currentConnections 
                       objectAtIndex:indexPath.row 
                       ] objectForKey:@"lastname" 
                      ];

NSString *fullname = [ firstname stringByAppendingString:lastname ];

cName.text = fullname;

[ cell.contentView addSubview:cName ];
[ cName release ];



cell.imageView.image = [[ currentConnections 
                     objectAtIndex:indexPath.row 
                     ] objectForKey:@"profilepicture" 
                    ];



UILabel *cTitle = [[ UILabel alloc ]
                 initWithFrame:CGRectMake(105, 25, 190, 13)
                 ];
cTitle.font = [ UIFont 
             fontWithName:@"ArialMT" 
             size:10 
             ];
cTitle.textColor = [ UIColor whiteColor ];
cTitle.backgroundColor = [ UIColor clearColor ];
cTitle.text = [[ currentConnections 
                  objectAtIndex:indexPath.row 
                  ] objectForKey:@"title" 
                 ];


[ cell.contentView addSubview:cTitle ];
[ cTitle release ];

return cell;

}

If you are reusing cells and everytime you setup a cell you are adding a UILabel to it then you can see how a lot of UILabels are being allocated that may cause your memory issue...If you want to add a label to the cell, you should only have to do this once when you allocate it, then if the cell is coming in as a reused cell, it would already contain the UILabel that you added, so you would only have to edit the labels text instead of adding a new UILabel... this is just a guess from what you have posted, some more code might clarify things...

The problem is apparent from the code you posted, you are leaking the cell instance variable, also keep in mind that cellForRowAtIndexPath gets called for every row in the table, so keeping one instance variable to keep track of something added to the cell will not work, the var will be keep being overwriten, but in your case you are leaking cells. You are adding cells to the UITableViewCell, never releasing them, and then on the next call to cellForRowAtIndexPath the cells var gets overwriten and the previous one leaks because you dont have a way to release it anymore...You can fix this by releasing cells...

Now you also have the issue of using one var to keep track of labels on cells, keep in mind that it's not one cell being allocated for reuse, it really is as many cells as can show on the screen, so keeping track of one var will not work is your issue...To solve that problem i would subclass UITableViewCell with a label property and have a method to reset the label text..

Hope this helps

  • there are so many contact that you cannot load all in the same array?
  • you could use an NSMutableArray and method removeAllObjects.
  • in your code:

    [ cell addSubview:firstname ]; firstname is a string...you cannot add an NSString as subview...and i recommend creating a custom UITableviewcell because cell are recycled and when you use a recycled cell there are inside all subview added during his creation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM