【问题标题】:Cached UIImages fetching performance in UITableView缓存的 UIImages 在 UITableView 中获取性能
【发布时间】:2010-08-10 18:42:49
【问题描述】:

我已经使用@atebits 的ABTableViewCell 有一段时间了。对于不熟悉的人来说,基本上它通过绘制所有标签和图像,而不是分配 UIKit 对象(如 UILabels、UIImageView 等),使 tableview 滚动快得多

现在,我有相当大的UITableViews,在每个单元格中,都有一个小的 (32x32) 用户图像和一个中等 (75x75) 的照片缩略图。该绘图确实有助于使此 tableview 快速滚动,但我仍在尝试找出获取这些要在单元格内绘制的图像的最佳方法。

我查看了 Apple 的 LazyTableView 示例,但我仍然不确定执行此操作的最佳方法是什么。我只知道cellForRowAtIndexPath: 中的代码/分配越少越好。

所以我的图像要么从服务器下载,要么从我的 CoreDataHelper 中检索。我的问题是:以下哪种方法更好地检索缓存的图像

  1. cellForRowAtIndexPath: 中使用NSFetchRequest 来获取缓存的图像(因此在tableview 滚动时分配NSFetchRequest 实例)。

  2. 将所有缓存的图像从一开始就保留在 tableView 的数据源中,这样我就可以从我的UITableViewController 中的(viewDidLoad: 中已分配的数组)数据源中获取它们。这种方法显然会占用更多内存,因为它分配了一堆小图像,但我猜滚动最终会更流畅。

任何人都可以启发我吗?

(顺便说一句,我希望图像显示在 tableview 滚动时。等待它停止滚动以加载图像不是一种选择)。


编辑:从磁盘加载图像

按照提供的答案,我现在尝试使用图像本地路径直接从磁盘加载图像(显然是在保存之后),该路径是 tableview 数据源的一部分。这是cellForRowAtIndexPath: 方法中的代码:

NSData *imageData = [NSData dataWithContentsOfFile:imageLocalPath];     
UIImage *userImage = [UIImage imageWithData:imageData];
cell.avatarImage = userImage;

它仍然不是很顺利(每次出现新单元格时都会有一个小滞后(每次调用 cellForRowAtIndexPath:)我应该异步进行此调用吗?或者磁盘读取速度是否足够快并且绝对不是这里的问题?


编辑:更多细节

1.你从哪里得到 imageLocalPath?

图像 url 是我的 dataSource 的一部分,它是在 viewDidLoad 中分配的 NSArray(缓存到 CoreData 但不应该影响滚动)。 app 文档目录路径也保留在 viewDidLoad 中,因此整个文件路径在 cellForRowAtIndexPath 中使用

NSArray *urlArray = [picURLString componentsSeparatedByString: @"/"];
NSString *filePath = [NSString stringWithFormat:@"%@/%@", documentsPath, [urlArray lastObject];

2。当你通过鲨鱼运行它时会发生什么?

我以前从未使用过 Shark,尝试使用它,但显然 iOS 4 不再支持它。你能证实这一点吗?当我点击“开始”按钮时,什么也没有发生。

3.您在设备上的 Instruments 中获得的 FPS 是多少?

我的速度在 25 到 30fps 之间,但在实际屏幕上看起来并不那么流畅。不确定这是否意味着什么。我觉得我得到了10-15fps。同样,单元格之间的滚动是平滑的,只是当 tableview 碰到一个新单元格时会有轻微的延迟(滚动速度如此之快 = 每秒有很多这样的小延迟)。

【问题讨论】:

    标签: iphone performance uitableview core-data


    【解决方案1】:

    您确定图片是您的问题吗?您是否对您的桌子进行了 alpha 检查?以我的经验,加载图像几乎从来都不是表格滚动的性能,它几乎总是 alpha 通道问题。

    在走这条路之前,我会先在 Instruments 中打开 alpha 测试。

    更新

    我想知道您首先加载数据是否有障碍:

    UIImage *userImage = [UIImage imageWithContentsOfFile:imageLocalPath];
    [cell setAvatarImage:userImage];
    

    应该UIImage为你做一些缓存工作。

    • 您从哪里获得 imageLocalPath?
    • 是存储在核心数据实体中还是正在进行计算?
    • 当你通过鲨鱼运行这个时会发生什么?
    • 您在设备上的 Instruments 中获得了多少 FPS?

    更新 2

    您为什么要获取 Core Data 对象并将它们存储到 NSArray 中?在大多数情况下,您应该使用NSFetchedResultsController

    您不应该构建文件路径,这需要时间并且-componentsSeparatedByString: 正在执行字符串搜索;非常慢。 存储保存图像时的文件路径。快多了。如果您不想更改模型,请将其设置为在 -awakeFromFetch: 上解析的瞬态值。

    Shark 的功能自 iOS4 起与 Instruments 集成。

    25-30 FPS 并不理想,但相当不错。这意味着您获得了良好的帧速率,而细微的调整(对于主要的基本问题)可能会提高它。

    【讨论】:

    • 我所有的表格视图都是绿色的。不过这是一个很好的测试。还是谢谢。
    • 我敢打赌,加载图像仍然不是性能问题的原因。由于您的 tableview 是绿色的,那么下一步将是预加载图像并查看这是否会产生性能差异,如果没有(我怀疑)那么您可以将其从列表中划掉。
    • 我刚刚测试了预加载图像的滚动(第二次),滚动显着改善。我的问题是针对图像缓存/获取的,因为我已经进行了 alpha/opaque 和预加载的图像测试(尽管我可能错过了一个)。
    • 有趣,将有助于发布代码。但与此同时:我会将图像存储在磁盘上,并将对它们的文件引用放在单元格所代表的对象中。而不是使用NSFetchRequest(这会减慢速度),只需从对象中获取路径并从磁盘加载它。 UIImage 将在内部缓存图像(有点)。
    • 您好,感谢您的跟进。上面贴了一些代码。让我知道您认为可能出错的地方。
    【解决方案2】:

    保留所有图像以供参考是不可扩展的。以这种方式杀死旧的 iPod 并不需要太多。

    根据我的经验,CoreData 只是一个慢动作。在滚动时将 CoreData 对象通过管道传输到 tableview 中并不是一个好主意。

    我的解释是您将二进制图像存储在 CoreData 中?那可能是你的问题。您可能只需要在此处信任磁盘 I/O。我是一个应用程序的领导者,它的缩略图是你的两倍大,我通过将数据写入磁盘上的文件并检索它来手动完成所有缓存......如果它不在缓存中,我使用 ASIHTTPRequest 来拉。

    它在 iPhone 上非常流畅,即使在必须上网查看某些图像时也是如此。只要你异步读取缓存就可以了。

    【讨论】:

    • 是的,现在二进制图像存储在 CoreData 中。将它们写入磁盘听起来像是真正的交易。您是否有机会分享一下您如何存储和获取图像?
    • Core Data 并不慢,除非你做错了。例如,在-cellForRowAtIndexPath 中执行NSFetchRequest 是错误的。
    • @Jasconius 我真的很好奇你是怎么做到的,所以它是黄油光滑的。你能看看我编辑的问题中的代码,如果它与你所做的相似,请告诉我?
    • @Marcus 是的,这就是我的意思.. 获取 cellForRow 听起来像是他正在做的事情,因为他的图像是核心数据。我的意思是......如果你的图像在 CoreData 中,你会怎么做? @Sam 您在问题中提出的内容非常接近。我所拥有的是一个名为“检查缓存”的方法,它返回一个 UIImage,如果 UIImage 为 nil(意味着它不在缓存中),我会在网上找到它。其实我正好在主线程上做缓存检查,就是这么快。
    • 他已经在内存中有 Core Data 对象,所以不需要获取,只需访问它。让 Core Data 处理数据,这就是它的设计目的。顺便说一句,ASIHTTPRequest 远比花 10 分钟在NSURLConnection 上实现自己的委托要重得多——我不推荐它。
    【解决方案3】:

    我不太熟悉使用 CoreData,但我确实认为 CoreData 是某种缓存,所以无论您自己缓存它还是使用 NSFetchRequest,它最终都会在内存中以便快速访问。另外,我不太喜欢 CoreData,因为它本身就是一个数据库,必须与服务器保持最新,这最终导致效率低下(这是我自己的经验)

    就个人而言,我会选择 #2 的路线。因为您可以使用类似ASIHTTPRequest 框架的东西来执行所有支持缓存和多个其他选项(如队列)的传输操作。

    附带说明,在滚动停止后出现图像的问题仅仅是由于 UIScrollView 滚动操作发生在主运行循环之外,您可以通过在当前运行循环上调用绘图操作来轻松解决此问题,它们将在滚动时出现(如果您在每个单元格上执行一些繁重的操作,这显然会影响性能)

    【讨论】:

    • 滚动发生在主线程上。动画没有,但你会得到滚动回调。
    • 是的,它发生在主线程上,但不在主运行循环
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-06
    • 2012-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-17
    相关资源
    最近更新 更多