带图像的UITableView滚动速度非常慢

ani*_*mar 4 iphone scroll uitableview ios

可能重复:
带图像的表视图,慢速加载和滚动

我有一个 从服务器UITableView下载图像UITableViewCells.我发现tableView的滚动速度非常慢.

我认为这可能会下载问题,但我已经意识到下载完成后表格仍然会慢慢滚动,图像图标大小较小.

我搜索了谷歌但找不到任何帮助.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    btnBack.hidden = FALSE;

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil)
    {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        cell.backgroundColor = [UIColor clearColor];

        cell.textLabel.font = [UIFont fontWithName:@"Noteworthy" size:17.0];
        cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
        cell.textLabel.textColor = [UIColor blackColor];
        cell.textLabel.highlightedTextColor = [UIColor blackColor];
    }

        cell.textLabel.text = [NSString stringWithFormat:@"     %@", [test.arrTitle objectAtIndex:indexPath.row]];

        NSString *Path;
        Path = [NSString stringWithFormat:@"http://%@",[test.arrImages objectAtIndex:indexPath.row]];
        NSLog(@"image-->%@",[test.arrImages objectAtIndex:indexPath.row]);
        NSString *strImage = Path;
        NSURL *url4Image = [NSURL URLWithString:strImage];    
        NSData *data = [NSData dataWithContentsOfURL:url4Image];
        image =[[UIImage alloc] initWithData:data];
        cell.imageView.image =image;
        [image release];

        return cell;
}
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 18

虽然我在下面的原始答案试图解决与异步图像检索相关的几个关键问题,但它仍然基本上受到限制.正确的实现还将确保如果您快速滚动,可见单元格优先于滚动屏幕的单元格.它还支持取消先前的请求(并在适当时取消它们).

虽然我们可以添加这些各种各样的功能,下面的代码,它可能是更好的采取一个既定的,成熟的解决方案,利用NSOperationQueueNSCache下面讨论的技术,而且还解决了上述问题.最简单的解决方案是采用UIImageView支持异步图像检索的已建立类别之一.该AFNetworkingSDWebImage库都有UIImageView那妥善处理所有这些问题的类别.


您可以使用NSOperationQueueGCD进行延迟加载(有关不同异步操作技术的讨论,请参阅" 并发编程指南").前者具有以下优点:您可以精确指定允许的并发操作数量,这对于从Web加载图像非常重要,因为许多Web服务器限制了它们将从给定客户端接受的并发请求数.

基本思路是:

  1. 在单独的后台队列中提交图像数据的请求;
  2. 完成下载图像后,将UI更新回主队列,因为您不应该在后台进行UI更新;
  3. 在主队列上运行调度的最终UI更新代码时,请确保UITableViewCell它仍然可见并且尚未出列并重复使用,因为有问题的单元格已从屏幕上滚动.如果你不这样做,可能会暂时出现错误的图像.

您可能希望使用以下代码替换代码:

首先,为您NSOperationQueue将用于下载图像的属性定义一个属性,以及NSCache用于存储这些图像的属性:

@property (nonatomic, strong) NSOperationQueue *imageDownloadingQueue;
@property (nonatomic, strong) NSCache *imageCache;
Run Code Online (Sandbox Code Playgroud)

其次,初始化此队列并缓存在viewDidLoad:

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.imageDownloadingQueue = [[NSOperationQueue alloc] init];
    self.imageDownloadingQueue.maxConcurrentOperationCount = 4; // many servers limit how many concurrent requests they'll accept from a device, so make sure to set this accordingly

    self.imageCache = [[NSCache alloc] init];

    // the rest of your viewDidLoad
}
Run Code Online (Sandbox Code Playgroud)

第三,你cellForRowAtIndexPath可能看起来像:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    btnBack.hidden = FALSE;

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;

        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        cell.backgroundColor = [UIColor clearColor];

        cell.textLabel.font = [UIFont fontWithName:@"Noteworthy" size:17.0];
        cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
        cell.textLabel.textColor = [UIColor blackColor];
        cell.textLabel.highlightedTextColor = [UIColor blackColor];
    }

    cell.textLabel.text = [NSString stringWithFormat:@"     %@", [test.arrTitle objectAtIndex:indexPath.row]];

    // code change starts here ... initialize image and then do image loading in background

    NSString *imageUrlString = [NSString stringWithFormat:@"http://%@", [test.arrImages objectAtIndex:indexPath.row]];
    UIImage *cachedImage = [self.imageCache objectForKey:imageUrlString];
    if (cachedImage) {
        cell.imageView.image = cachedImage;
    } else {
        // you'll want to initialize the image with some blank image as a placeholder

        cell.imageView.image = [UIImage imageNamed:@"blankthumbnail.png"];

        // now download in the image in the background

        [self.imageDownloadingQueue addOperationWithBlock:^{

            NSURL *imageUrl   = [NSURL URLWithString:imageUrlString];    
            NSData *imageData = [NSData dataWithContentsOfURL:imageUrl];
            UIImage *image    = nil;
            if (imageData) 
                image = [UIImage imageWithData:imageData];

            if (image) {
                // add the image to your cache

                [self.imageCache setObject:image forKey:imageUrlString];

                // finally, update the user interface in the main queue

                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    // Make sure the cell is still visible

                    // Note, by using the same `indexPath`, this makes a fundamental
                    // assumption that you did not insert any rows in the intervening
                    // time. If this is not a valid assumption, make sure you go back
                    // to your model to identify the correct `indexPath`/`updateCell`

                    UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath];
                    if (updateCell)
                        updateCell.imageView.image = image;
                }];
            }
        }];
    }

    return cell;
}
Run Code Online (Sandbox Code Playgroud)

第四,最后,虽然有人可能倾向于在低内存情况下编写代码来清除缓存,但事实证明它是自动完成的,因此这里不需要额外的处理.如果您在模拟器中手动模拟低内存情况,则不会看到它驱逐其对象,因为NSCache没有响应UIApplicationDidReceiveMemoryWarningNotification,但在实际操作期间,当内存不足时,将清除缓存.实际上,NSCache不再优雅地响应低内存情况本身,所以你真的应该为这个通知添加观察者并在低内存情况下清空缓存.

我可能会建议一系列其他优化(例如,也许还可以将图像缓存到持久存储中以简化未来的操作;我实际上将所有这些逻辑放在我自己的AsyncImage类中),但首先看看这是否解决了基本的性能问题.