Memory issues when displaying large images in a UITableView

I have a system that downloads a large number of large images from the Internet and displays them in custom table cells. On older devices, memory warnings are pretty fast, so I implemented a system to delete some from the table to try to deal with it, but it did not work well enough (a large number of images were deleted, which affected the user interface).

So, I thought that I could upload all the images to the device’s cache and then upload them from there - I implemented SDWebImage , This is great, but I still have not solved the memory allocation problem, since the images are displayed all the time and therefore are stored in memory causing failures.

It seems to me that I need to implement a system that shows images (from the cache), if the cell is displayed and hide it, if the cell is not displayed - I just got stuck on how to build such a system.

Or will it not work? Can you keep application memory low (and stop it with memory alerts / crashes) by deleting images from your table cells? Or do I just need to continue my previous decision and just delete images / cells until the memory warnings stop?

Updated with code

TableViewController.m

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { if (indexPath.section == 0) { currentIndexPath = indexPath; ImageTableCell *cell = (ImageTableCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; ImageDownloader *download = [totalDownloads objectAtIndex:[indexPath row]]; if (cell == nil) { cell = [[[ImageTableCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier: CellIdentifier] autorelease]; } cell.imageView.image = download.image; return cell; } return nil; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { int t = [totalDownloads count]; return t; } 

ImageTableCell.m - Custom Cell

 - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { self.frame = CGRectMake(0.0f, 0.0f, 320.0f, 0.0f); self.contentView.frame = CGRectMake(0.0f, 0.0f, 320.0f, 0.0f); self.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth); self.contentMode = UIViewContentModeScaleToFill; self.autoresizesSubviews = YES; self.contentView.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth); self.contentView.contentMode = UIViewContentModeScaleToFill; self.contentView.autoresizesSubviews = YES; [self.imageView drawRect:CGRectMake(0.0f, 0.0f, 320.0f, 0.0f)]; self.imageView.contentMode = UIViewContentModeScaleAspectFill; self.imageView.autoresizingMask = (UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth); self.imageView.opaque = YES; } return self; } 

ImageDownloader (implements SDWebImageManagerDelegate)

  -(void) downloadImage // Comes from Model class { if (image == nil) { NSURL *url = [NSURL URLWithString:self.urlString]; SDWebImageManager *manager = [SDWebImageManager sharedManager]; // Remove in progress downloader from queue [manager cancelForDelegate:self]; if (url) { [manager downloadWithURL:url delegate:self retryFailed:YES]; } } } - (void)cancelCurrentImageLoad { [[SDWebImageManager sharedManager] cancelForDelegate:self]; } - (void)webImageManager:(SDWebImageManager *)imageManager didFinishWithImage:(UIImage *)_image { self.image = _image; if ([self.delegate respondsToSelector:@selector(addImageToModel:)]) [self.delegate addImageToModel:self]; } - (void)webImageManager:(SDWebImageManager *)imageManager didFailWithError:(NSError *)error; { if ([self.delegate respondsToSelector:@selector(badImage)]) [self.delegate badImage]; } 
+6
source share
4 answers

Instead of using [UIImage imageNamed:@""] try [[[UIImage alloc] initWithContentsOfFile:@""] autorelease];

Edit:

Fine I went through SDWebImage. Use NSAutoreleasePool , wherever you find that a new thread has appeared.

And another solution is to resize the image before saving it to the cache.

+1
source

After loading images, do not store large images in memory. just create a small image size (thumbnail) to display in the form of a table and write the enlarged image in a directory.

You can create a thumbnail of your image using the following code.

  CGSize size = CGSizeMake(32, 32); UIGraphicsBeginImageContext(size); [yourImage drawInRect:CGRectMake(0, 0, 32, 32)]; yourImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); 
+1
source

Thus, as soon as the image is loaded, it remains an SDWebImage instance in it and is never freed. You must save your image on iPhone using:

 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsPath = [paths objectAtIndex:0]; NSString *imagePath = [documentsPath stringByAppendingPathComponent:[NSString stringWithFormat:@"image%d.jpg", rowNumber]; // you should have this set somewhere [UIImageJPEGRepresentation(_image, 1.0) writeToFile:imagePath atomically:YES]; 

and save only the path to it in your SDWebImage instance. Then in -cellForRowAtIndexPath: instead of doing:

 cell.imageView.image = download.image; 

you should do something like:

 UIImage *image = [[UIImage alloc] initwithContentsOfFile:download.imagePath]; cell.imageView.image = image; [image release]; 

This will always load the image from disk, and since cell.imageView.image is a stored property, after it fades out or is reused, it will clear the image from memory.

+1
source

I would like to know how you upload images, do you use custom cells? If so, please go through the Apple UITableView Programming Guide. They clearly tell us how to upload images. In this they say that we need to draw images in order to avoid memory problems. The best example of how to upload images is in the Apple Sample Code LazyTableImages example. Please go through this too.

0
source

Source: https://habr.com/ru/post/893677/


All Articles