views:

239

answers:

4

I simplified my code to test with, and still on the phone my memory usage keeps climbing to a point where the table slows way down.

Can someone tell me what I'm doing wrong here?

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;

}

  • (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 40; }

  • (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 100; }

  • (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellID = @"Cell"; [self.tableView deselectRowAtIndexPath:indexPath animated:YES]; UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellID]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellID] autorelease]; } UILabel *l=[[UILabel alloc] initWithFrame:CGRectMake(10,10,300,16)]; l.font=[UIFont boldSystemFontOfSize:15]; l.textColor=[UIColor whiteColor]; l.backgroundColor=[UIColor blackColor]; l.text=@"Just some randoom text here"; [cell.contentView addSubview:l]; [l release];

A: 

Oops. That code paste didn't work too well. Here's a straight paste:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 40;

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100;

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellID = @"Cell";
    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellID];
    if (cell == nil) {
     cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellID] autorelease];
    }
    UILabel *l=[[UILabel alloc] initWithFrame:CGRectMake(10,10,300,16)];
    l.font=[UIFont boldSystemFontOfSize:15];
    l.textColor=[UIColor whiteColor];
    l.backgroundColor=[UIColor blackColor];
    l.text=@"Just some randoom text here";
    [cell.contentView addSubview:l];
    [l release];
        return cell;
}
A: 

You're recycling UITableViewCell instances, but you're still creating a new UILabel instance for every row, and adding it to each and every cell. This is where the memory usage comes from. Try something like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *cellID = @"Cell";
    [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellID];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellID] autorelease];
        UILabel *l=[[UILabel alloc] initWithFrame:CGRectMake(10,10,300,16)];
        l.font=[UIFont boldSystemFontOfSize:15];
        l.textColor=[UIColor whiteColor];
        l.backgroundColor=[UIColor blackColor];
        l.text=@"Just some randoom text here";
        [cell.contentView addSubview:l];
        [l release];
    }
    return cell;
}
drvdijk
+1  A: 

You will want to follow a pattern like this:

#define kTagMyLabel 1

- (UITableViewCell *)tableView:(UITableView *)tableView 
         cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
  static NSString *cellID = @"Cell1";
  UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellID];
  UILabel * l;
  if (cell == nil) {
    // create the cell
    cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:cellID] autorelease];

    // perform setup/functions that are common to all cells
    l = [[[UILabel alloc] initWithFrame:CGRectMake(10,10,300,16)] autorelease];
    l.font=[UIFont boldSystemFontOfSize:15];
    l.textColor=[UIColor whiteColor];
    l.backgroundColor=[UIColor blackColor];
    l.tag = kTagMyLabel ;
    [cell.contentView addSubview:l];
  }
  else
  {
    // find the label we previously added.
    l = (UILabel*)[cell viewWithTag:kTagMyLabel];
  }

  // now set up the cell specific to this indexPath
  l.text=@"Just some random text here";
  return cell;
}
Jason
Unfortunately memory pegs with this solution also (straight copy and paste). Maybe it's the way the tableview is placed. I created the tabbar controller in IB, and assign the view controllers in IB also. So maybe the autoreleasepool never gets a chance to release? What would be a better way? And should I be putting an NSAutoreleasePool somewhere other than main to free this up? If so, where given that the view itself being at the tabbar position doesn't release?
Don't know if this means anything, but memory frees properly in the Simulator.
The pool is drained after every UI event. What *exactly* is not being released? i.e. when do you see a memory drain? Is it as you scroll down a long list of items, and then back out of your viewcontroller? You have a leak somewhere else then.
Jason
A: 

Since every UITableViewCell has its own standard UILabel (cell.textLabel), do you really need that extra label added to the contentView? If you need custom cells, you might want to consider subclassing UITableViewCell.

RoelandVL
The example I gave was just a paired down version. In my real code, I do subclass UITableviewCell and add several fields and an image.