表视图滚动异步 [英] Table View Scrolling Async

查看:52
本文介绍了表视图滚动异步的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将图像加载到表格视图单元格中,每个单元格都有一个图像。我已经将几个教程转换为下面的代码,但我仍然放慢速度。

I am loading an image to a table view cell, each cell has an image. I've adapter a couple tutorials to the code below, but I am still having slow down.

我从文档目录加载这些图像。有关如何加快此过程的任何提示或想法?

I am loading these images from the documents directory. Any tips or ideas on how to speed this process up?

编辑修订代码:

Beer *beer = (Beer *) [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.displayBeerName.text = beer.name;

// did we already cache a copy of the image?
if (beer.image != nil) {
    // good.  use it.  this will run quick and this will run most of the time
    cell.beerImage.image = beer.image;
} else {
    // it must be the first time we've scrolled by this beer.  do the expensive
    // image init off the main thread

    cell.beerImage.image  = nil;   // set a default value here.  nil is good enough for now

    [self loadImageForBeer:beer atIndexPath:indexPath];
}
- (void)loadImageForBeer:(Beer *)beer atIndexPath:(NSIndexPath *)indexPath {

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
    dispatch_async(queue, ^{

        UIImage *image = [UIImage imageWithContentsOfFile:beer.imagePath];
        beer.image = image;

        dispatch_sync(dispatch_get_main_queue(), ^{
            UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
            cell.beerImage.image = image;
        });
    });
}


推荐答案

你的算法看起来很不错。你已经避免了许多典型的陷阱。如果您仍然遇到UI性能问题,我建议您做一些事情:

Your algorithm looks pretty good. You've avoided many of the typical pitfalls. If you're still having UI performance problems, I'd suggest a couple of things:


  1. 您应该尝试缓存图片在记忆中。你可以使用 NSMutableArray NSMutableDictionary ,但是在在ios应用程序上缓存图像的最佳方法是什么? Caleb讨论了 NSCache <的优点/ code> class,简化了流程。如果您确实缓存了图像,请确保响应内存压力并在必要时清除缓存。您可以回复 didReceiveMemoryWarning 或将自己添加为通知中心的 UIApplicationDidReceiveMemoryWarningNotification

  1. You should try caching your images in memory. You could use NSMutableArray or NSMutableDictionary, but at Best way to cache images on ios app? Caleb discusses the merits of the NSCache class, which simplifies the process. If you do cache images, make sure you respond to memory pressure and purge the cache if necessary. You can respond to didReceiveMemoryWarning or add yourself as an observer to the notification center's UIApplicationDidReceiveMemoryWarningNotification.

确保您的缓存图像是缩略图大小的,否则您的UI中总会有一点口吃(如果您需要调整大小算法,请告诉我们)并且它会耗尽内存不必要的;

Make sure your cached images are thumbnail sized or else you'll always have a little stuttering in your UI (if you need a resizing algorithm, let us know) and it will use up memory unnecessarily;

当您将映像更新发送回主队列时,您应该异步执行此操作(为什么该后台队列会在等待时占用资源并占用资源将块发送回主队列以完成...一旦在快速滚动期间备份了几个图像,这尤其是一个问题;和

When you dispatch your image update back to the main queue, you should do so asynchronously (why have that background queue hang around and tie up resources as it waits for the the block to be sent back to the main queue to finish ... this is especially an issue once you have a couple of images backed up during a fast scroll); and

当你发送回主队列时,你应该检查以确保从 cellForRowAtIndexPath 不是 nil (因为如果单元加载逻辑太过备份(特别是在较慢的设备上),理论上你可以让有问题的单元滚出屏幕而你的算法可能会崩溃)。

When you dispatch back to the main queue, you should check to make sure cell you get from cellForRowAtIndexPath is not nil (because if cell loading logic gets too backed up (esp on slower devices), you could theoretically have the cell in question having scrolled off the screen and your algorithm could crash).

我使用的算法与您的算法非常相似,具有几乎相同的GCD结构(使用以上警告)并且它非常流畅,即使在较旧的设备上也是如此。如果你想让我发布代码,我很高兴。

I use an algorithm very much like yours, with almost the same GCD structure (with the above caveats) and it's pretty smooth scrolling, even on older devices. If you want me to post code, I'm happy to.

如果您仍然遇到麻烦,CPU分析器非常适合识别瓶颈并让您知道应该集中注意力的位置。在线提供了一些很棒的WWDC会议,重点是如何使用仪器来识别性能瓶颈,我发现它们对于熟练使用仪器非常有帮助。

If you're still having troubles, the CPU profiler is pretty great for identifying the bottlenecks and letting you know where you should focus your attention. There are some great WWDC sessions available online which focus on how to use Instruments to identify performance bottlenecks, and I found them to be very helpful to gain proficiency with Instruments.

这里是我的代码。在 viewDidLoad 中,我初始化了我的图像缓存:

Here is my code. In viewDidLoad, I initialize my image cache:

- (void)initializeCache
{
    self.imageCache = [[NSCache alloc] init];
    self.imageCache.name = @"Custom Image Cache";
    self.imageCache.countLimit = 50;
}

然后我在我的 tableView中使用它:cellForRowAtIndexPath

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ilvcCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    // set the various cell properties

    // now update the cell image

    NSString *imagename = [self imageFilename:indexPath]; // the name of the image being retrieved

    UIImage *image = [self.imageCache objectForKey:imagename];

    if (image)
    {
        // if we have an cachedImage sitting in memory already, then use it

        cell.imageView.image = image;
    }
    else
    {
        cell.imageView.image = [UIView imageNamed:@"blank_image.png"];

        // the get the image in the background

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

            // get the UIImage

            UIImage *image = [self getImage:imagename];

            // if we found it, then update UI

            if (image)
            {
                dispatch_async(dispatch_get_main_queue(), ^{

                    // if the cell is visible, then set the image

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

                    [self.imageCache setObject:image forKey:imagename];
                });
            }
        });
    }

    return cell;
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];

    [self.imageCache removeAllObjects];
}

顺便说一下,您可能考虑的另一个优化是预加载您的缓存图像在一个单独的队列中,而不是在一个单独的线程中及时加载图像。我觉得这不是必要的,因为这似乎对我来说足够快,但它是加速用户界面的另一个选择。

As an aside, one further optimization that you might contemplate would be to preload your cached images in a separate queue, rather than loading images in a separate thread just-in-time. I don't think it's necessary, as this seems to be more than fast enough for me, but it's one more option to speed up the UI.

这篇关于表视图滚动异步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆