views:

551

answers:

2

SO,

I have a tableview with 20 cells each with a text title, a subtitle label and a textfield (with a button to give it a nice background), but I find that after scrolling the tableview a few times it starts to slow down in the simulator. I figure this is a memory leak, but I thought I'd already released everything that I needed. Any comments on my (amateur) code?

Note: I had previously tried to draw boxes around the textviews using roundedcorners in Quartz, but this had a dramatic slow down on the box scrolling too. Please have a look for the memory leak before you destroy my code ;P

Note2: PS Sorry for being liberal with the code, I can't find the leak.

Regards

    // Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {


    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
        CGRect frame; frame.origin.x = 5; frame.origin.y = 10; frame.size.width = 20; frame.size.height = 25; 

        //Disallow Selection (Blue Flash)
        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        //Print Icon
        UIImageView *imgLabel = [[UIImageView alloc] initWithFrame:frame];
        imgLabel.tag = 1;
        [cell.contentView addSubview:imgLabel];
        [imgLabel release];

        //Print Text
        frame.origin.x = 30; 
        frame.origin.y = 5;
        frame.size.width = CONST_Cell_width;    
        UILabel *nameLabel = [[UILabel alloc] initWithFrame:frame];
        nameLabel.tag = 100;
        [cell.contentView addSubview:nameLabel];
        [nameLabel release];

        //subtitleLabel Text
        UILabel *subtitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(30, 32, CONST_Cell_width, 40)];
        subtitleLabel.tag = 101;
        [cell.contentView addSubview:subtitleLabel];
        [subtitleLabel release];



//      detailLabel.layer.cornerRadius = 10;


    } 
    //This bit is good!
    //cell.textLabel.text         = [[contentArray objectAtIndex:indexPath.row] objectForKey:@"Title"];
    //cell.detailTextLabel.text = [[contentArray objectAtIndex:indexPath.row] objectForKey:@"Description"];
    //Sets wrapping correctly
    //cell.textLabel.numberOfLines = 0;
    //cell.detailTextLabel.numberOfLines = 0;



    //Setup Name to Cell
    UILabel * nameLabel = (UILabel *) [cell.contentView viewWithTag:100];
    [nameLabel setFont:[UIFont boldSystemFontOfSize:20.0]];
    nameLabel.text = [[contentArray objectAtIndex:indexPath.row] objectForKey:@"Title"];
    nameLabel.textColor = [UIColor blackColor];
    nameLabel.backgroundColor = [UIColor clearColor];
    nameLabel.numberOfLines = 1;

    //Setup Subtitle
    UILabel * subtitleLabel = (UILabel *) [cell.contentView viewWithTag:101];
    [subtitleLabel setFont:[UIFont systemFontOfSize:15.0]];
    subtitleLabel.textColor = [UIColor grayColor];
    subtitleLabel.text = [[contentArray objectAtIndex:indexPath.row] objectForKey:@"Description"];
    subtitleLabel.backgroundColor = [UIColor clearColor];
    subtitleLabel.numberOfLines = 2;


        //A Nice Button for rounded background effect..cheap i know
        UIButton *roundedButtonType = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
        roundedButtonType.frame = CGRectMake(30, 80, CONST_Cell_width, CONST_Text_height);
        roundedButtonType.userInteractionEnabled = NO;
        roundedButtonType.backgroundColor = [UIColor clearColor];
        [cell.contentView addSubview:roundedButtonType];
        [roundedButtonType release];

        UITextView *detailLabel = [[UITextView alloc] initWithFrame:CGRectMake(30, 80, CONST_Cell_width, CONST_Text_height)];
        detailLabel.tag = indexPath.row;
        detailLabel.backgroundColor = [UIColor clearColor];
        detailLabel.text = [NSString  stringWithFormat:@"%d",indexPath.row];
        detailLabel.font = [UIFont fontWithName:@"Helvetica" size:17];
        detailLabel.delegate = self;
        [cell.contentView addSubview:detailLabel];
        [detailLabel release];

        //NSLog(@"Made my way to cellForRowAtIndexPath. Enter some data!!!\n");
        //DataPoint Name
        NSString *keyname = [NSString stringWithFormat:@"data%d",indexPath.row +1];
        NSString *datavalue = [clientDataArray objectForKey:keyname];

        //DataValue (can be null)
        //NSString *dataValue = [clientDataArray objectForKey:keyname] ;
    if (datavalue == [NSNull null] ) datavalue = @"(blank)";



    detailLabel.text = datavalue;



    return cell;
    [datavalue release];
    [keyname release];
    [clientDataArray release];


}
A: 

Try running with the performance tool 'Leaks'

In Xcode, click Run > Run with Performance Tool > Leaks

Any leaks show up as red lines, you can select the section on the timeline where the leaks were detected and find the leaks by examining the stack. If that all sounds a bit complicated then simply stepping through your code and ensuring that you're releasing anything you create with alloc, new, copy or their variants you should catch most potential leaks.

By the way, a simple leak won't kill your app that quickly unless it's gone into a huge loop or something

Griffo
+1  A: 

All the code outside the if(cell == nil) loop is run for every visible cell, every time a user touches the tableView. Try putting an NSLog(@"I ran!"); just before return cell;

(the code after return will not be run, i.e. all your releasing of memory is not being done)

Ok, you are doing the right thing by given the views tags so you can reference them later on. Do all the UILabel stuff inside the if(cell == nil) loop as well and then outside the loop, before return cell use the tag to set the labels values:

((UILabel *)[cell viewWithTag:14]).text = [myDataArray objectAtIndex:indexPath.row];

Like this.

The code in the if(cell==nil) block is only run if the cell has not been displayed before, i.e. has been off-screen the whole time etc. The rest of the code is run all the time when using the tableView.

Hope it makes sense :)

RickiG