在UITableView中显示大图像的内存问题

时间:2011-07-27 11:18:14

标签: ios objective-c image uitableview memory-management

我有一个系统可以从网上加载很多大图像并将它们显示在自定义表格单元格中。在较旧的设备上,内存警告发生的速度非常快,所以我实现了一个删除表中的一些系统以试图解决这个问题的系统,但它不能很好地工作(很多图像被删除影响了UI)。

所以我想我可以将所有图片加载到设备的缓存中,然后从那里加载它们 - 我已经实现了SDWebImage。这很好,但我仍然没有解决内存分配的问题,因为图像仍然一直显示,因此保存在内存中 - 导致崩溃。

我认为我需要实现一个显示图像(来自缓存)的系统(如果正在显示单元格)并在单元格未显示时隐藏它 - 我只是坚持如何构建这样的系统。

或者这不起作用?你能通过从表格单元中删除图像来真正保持应用程序内存不足(并阻止内存警告/崩溃)吗?或者我只需要继续使用我之前的解决方案并删除图像/单元格直到内存警告停止?

已更新为代码

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 - 自定义单元格

- (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(实现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];
    }

4 个答案:

答案 0 :(得分:2)

不要使用[UIImage imageNamed:@""],请尝试[[[UIImage alloc] initWithContentsOfFile:@""] autorelease];

修改

精细。我已经浏览了SDWebImage。 只要您发现已生成新线程,请使用NSAutoreleasePool

还有一个解决方案是,在保存到缓存之前调整图像大小。

答案 1 :(得分:2)

下载图像后,请勿将大图像保留在内存中。只需创建一个小尺寸的图像(缩略图)即可在tableview中显示,并将较大的图像写入某个目录。

您可以使用以下代码创建图像的缩略图。

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

答案 2 :(得分:2)

所以基本上一旦下载了图像,它就会停留在它的SDWebImage实例中,永远不会被释放。您应该使用以下命令将图像保存到iPhone磁盘:

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];

并在SDWebImage实例中保留它的路径。然后在in -cellForRowAtIndexPath:方法而不是:

cell.imageView.image = download.image;
你应该做点什么:

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

这将始终从磁盘加载图像,因为cell.imageView.image是一个保留属性,一旦它被弄脏或重用,它将从内存中清理图像。

答案 3 :(得分:0)

我想知道你是如何加载图像的,你使用自定义单元格吗?如果是这样,请浏览Apple的UITableView Programming guide他们清楚地告诉我们如何加载图像。因为他们说我们应该绘制图像以避免内存问题。 关于如何加载图像的最佳示例在Apple的示例代码LazyTableImages中给出。请仔细阅读。