Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Soulstorm

macrumors 68000
Original poster
Feb 1, 2005
1,887
1
I have a table view, which has 2 labels and an image view. The image view will display images of 4kBytes in size.

However, when I add 20-30 cells, the iPhone struggles at scrolling, since it tries to rearrange the cells as they are moved upwards or downwards.

Is there any way to display the images in the table cells only in the cells that are currently displayed on screen? I know that there is a function on the tableview named "visibleCells", but I can't find a decent logic to implement.

Can anyone help me?
 
As far as I know the tableview is already doing what you suggest. The problem is probably to do with compositing and it might be better to pre-render the cell contents into a single view rather than make the tableview do all the work every time the table view is moved. There is an example on the dev site of how to do this.
 
As Troglodyte said, normally the tableViewController is already doing this for you. Unless you've somehow managed to bypass some of the efficiency coding of cellForRowAtIndexPath: that deals with dequeueReusableCellWithIdentifier: and reuseIdentifier:.
 
This is my code.

Code:
 static NSString *CellIdentifier;

UITableViewCell *cell;

if (self.imageThumbnailsEnabledInMainView == YES) {
     CellIdentifier = @"ImageSubtitleCell";
     cell = (ImageSubtitleCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
     if (cell == nil) {
NSArray *nib = [[NSBundle mainBundle]loadNibNamed:@"ImageSubtitleCell"  owner:selfoptions:nil];
          cell = [nib objectAtIndex:0];
     [self.activeCells addObject:cell];

     }
}else {
     CellIdentifier = @"Cell";
     cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
     if (cell == nil) {
          cell = [[[UITableViewCell alloc ]initWithStyle:UITableViewCellStyleSubtitlereuseIdentifier:CellIdentifier]autorelease];
//[self.activeCells addObject:cell];
     }
}

// Set up the cell...
NSManagedObject *managedObject;
if (tableView == searchDisplayController.searchResultsTableView) {
     managedObject = [self.filteredManagedOjects objectAtIndex:indexPath.row];
     cell.textLabel.textColor = [UIColor blackColor];
}
else{
     managedObject = [self.imagesArray objectAtIndex:indexPath.row];
     cell.textLabel.textColor = [UIColor whiteColor];
}

cell.textLabel.text = @"a Text";
cell.imageView.image = [self loadAppropriateImageForItem:[self.items objectAtIndex:indexpath.row"]];
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;

cell.autoresizesSubviews = YES;

cell.detailTextLabel.textColor = [UIColor lightGrayColor];
cell.detailTextLabel.text = @"secondaryText"
return cell;

Although I have fixed the problem with the slugginess (I had forgotten to set the "identifier" property in the NIB file) I have a new question:

As you can see, all images are loaded as the user scrolls up and down and the thumbnails are displayed. Is there any way I can deallocate the images inside the cells that are not displayed so that the memory allocated for the images can be kept low?

EDIT:

Problem not fixed. When scrolling up and down, the application continues to allocate memory. Help!
 
Is there any way I can deallocate the images inside the cells that are not displayed so that the memory allocated for the images can be kept low?
Perhaps. But is this really what you'd want? I think you need to make sure you strike a proper balance between memory footprint and image-loading performance. You don't want your scrolling to become sluggish again because you are having to reload all the images you deallocated. And since your images are pretty small in size (4K), I would think you'd rather keep them cached and taking up memory than uncaching them and burning processor to reload them constantly.
 
Perhaps. But is this really what you'd want? I think you need to make sure you strike a proper balance between memory footprint and image-loading performance. You don't want your scrolling to become sluggish again because you are having to reload all the images you deallocated. And since your images are pretty small in size (4K), I would think you'd rather keep them cached and taking up memory than uncaching them and burning processor to reload them constantly.

Thanks for the advice but...

1) Actually, as you can read in the edit... The problem was not fixed. When scrolling up and down, the application continues to allocate memory. Any ideas as to why this may happen?
2) Caching images is a good idea indeed, but what happens if the user has 150 images? Wouldn't this cause a slowdown?
 
Is there any way I can deallocate the images inside the cells that are not displayed so that the memory allocated for the images can be kept low?

That's not the way to do it. You should implement didReceiveMemoryWarning and release the images there. You don't show your code that loads the images. In fact if the image you assign to the cell is autoreleased then the only ones that will exist will be the ones visible, plus a few more.

In general you should use the memory available but clean things up in response to a memory warning.
 
I see. However, I would really like to know why the following happens: Here is my code:

in the ViewDidLoad I load the images array, and I cache the images
Code:
for (NSManagedObject *obj in self.imagesArray) {
		NSString *str =[obj valueForKey:@"filePath"];
		
		[self.cachedImages addObject:[UIImage imageWithContentsOfFile:str]];
	}

and this is my cellForRowAtIndexPath code:

Code:
UITableViewCell *cell;	
if (self.imageThumbnailsEnabledInMainView == YES) {
	CellIdentifier = @"ImageSubtitleCell";
	cell = (ImageSubtitleCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
	if (cell == nil) {
		NSArray *nib = [[NSBundle mainBundle]loadNibNamed:@"ImageSubtitleCell" owner:self options:nil];
		cell = [nib objectAtIndex:0];
	}
}else {
	CellIdentifier = @"Cell";
	cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
	if (cell == nil) {
		cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]autorelease];
	}
}

NSManagedObject *managedObject;
if (tableView == searchDisplayController.searchResultsTableView) {
	managedObject = [self.filteredManagedOjects objectAtIndex:indexPath.row];
	cell.textLabel.textColor = [UIColor blackColor];
}
else{
	managedObject = [self.imagesArray objectAtIndex:indexPath.row];
	cell.textLabel.textColor = [UIColor whiteColor];
}
	
cell.textLabel.text = [NSString stringWithFormat:@"%i : %@", indexPath.row, [managedObject valueForKey:@"imageName"]];
	
if (self.imageThumbnailsEnabledInMainView == YES) {
	cell.imageView.image = [self.cachedImages objectAtIndex:indexPath.row];
	cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
}
	
cell.autoresizesSubviews = YES;
	
cell.detailTextLabel.textColor = [UIColor lightGrayColor];
cell.detailTextLabel.text = [managedObject valueForKey:@"date"];
cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;

By running Instruments, I noticed that as I scroll up and down, the application is allocating more memory, even if all the cells have been shown at least once on the screen.

I know I am missing something simple here, I just can't find it!

EDIT: Most strange thing is that this happens on the iPhone, but not on the Simulator!
 
Allocating more memory to what? And where?

If you run the application with Instruments, and you see the "ObjectAlloc" instrument, you can see the memory consumption from objects allocated within the application.

You can also see the total RAM usage in the "CPU Monitor" instrument.

Total RAM usage is boosted up each time I scroll up and down the table view.

The strange thing is that the same thing happens with Apple's own sample code in TableViewSuite.
 
OK, something's wrong with your code. ObjectAlloc has a complete record of every object created by your app. What objects are piling up and not being released? Have you run the leaks tool?
 
As you can see, all images are loaded as the user scrolls up and down and the thumbnails are displayed. Is there any way I can deallocate the images inside the cells that are not displayed so that the memory allocated for the images can be kept low?

You could define a delegate object for your custom table cell. The cell could call out to the delegate when it needs an image to display. You can keep the images cached in your delegate and just draw the image with drawAtPoint: in a subview.

By running Instruments, I noticed that as I scroll up and down, the application is allocating more memory, even if all the cells have been shown at least once on the screen.

I noticed this line:

Code:
cell.textLabel.text = [NSString stringWithFormat:@"%i : %@", indexPath.row, [managedObject valueForKey:@"imageName"]];

Try caching an array of strings instead of creating them each time and see if that helps with the allocations.
 
PhoneyDeveloper OK, now I see how things work. "Leaks" does not show a memory leak, and my application's memory footprint is reduced greatly if I leave my tableview alone, after many seconds. So, I think that my problem is no more.

Thank you for your answers.

BruinEcon08, that's a very good advice, thank you. I hadn't thought about that.
 
Code:
cell.imageView.contentMode = UIViewContentModeScaleAspectFill

Also, from my own personal experience, you *really* want your content to be pre-rendered to the appropriate scale. The performance hit from having the OS scale each image in each cell while scrolling was considerable with one of my old projects.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.