问题描述
我正在开发一个 UITableView,它非常像 iOS 的原生照片应用程序:它有很多行,每行有 4 个图像缩略图.(即每个 UITableViewCell 有 4 个 UIImageViews)从核心数据加载的所有缩略图.
I am working on a UITableView that is very much like the iOS's native Photo app: it has many rows with 4 image thumbnails in each row. (i.e. each UITableViewCell has 4 UIImageViews) All thumbnails loaded from Core Data.
我已经多次修改我的实现,我可以看到性能改进,但它仍然无法像照片应用程序那样平滑滚动.
I have revised my implementation multiple times and I can see performance improvements, but it is still unable to scroll as smooth as the Photo app.
我需要有关如何正确缓存照片以获得最佳性能的建议.这是我试过的:
I need advise on how to properly cache the photos for best performance. This is what I tried:
1.我的第一次尝试(滚动时非常滞后)
- 图像以可转换类型存储在 CoreData 中.
- 在 cellForRow 函数中,每个图像都是从 CoreData 上获取的飞.
2.第二次尝试(更快,但滚动时仍然有点滞后)
- 图像以二进制数据类型存储,并在 CoreData 中勾选了外部存储"选项.
- 在 cellForRow 函数中,每个图像首先从 Core Data 加载,然后存储到内存中的 NSCache 中,因此下次 cellForRow 触发时,如果可用,我们将直接使用 NSCache 中的 UIImage.
使用 NSCache 缓存从 CoreData 加载的图像后,滚动明显更快,但由于图像在 NSCache 中尚不可用时仍必须从 CoreData 加载,因此滚动仍会时不时地抖动.
After using NSCache to cache images loaded from CoreData, scrolling is visibly faster but since images still have to be loaded from CoreData when it is not yet available in NSCache, scrolling will still be jerky from times to times.
所以,一定有更好的方法,我可以将所有图像预加载到内存中,但由于可能有大量或多行图像,所以我根本不打算预加载图像.
So, there must be a better way, I could preload all the images into memory but since there might be large number or rows of images so I didnt plan to preload the images at all.
我还能做些什么来更快地在 cellForRowAtIndexPath 中加载图像?
What else can I do to load the image faster in cellForRowAtIndexPath?
推荐答案
无论数据来自何处,为了保持滚动流畅,您需要在单独的线程上获取数据,并且只有在有数据时才更新 UI在记忆中.Grand Central Despatch 是必经之路.这是一个骨架,它假设您有一个 self.photos
字典,其中包含对图像文件的文本引用.图像缩略图可能会或可能不会加载到实时词典中;可能在也可能不在文件系统缓存中;否则从网上商店获取.它可以使用 Core Data,但平滑滚动的关键是你不必等待数据无论它来自哪里.
To keep your scrolling smooth regardless of where your data comes from, you need to fetch your data on a separate thread and only update the UI when you have the data in memory. Grand Central Despatch is the way to go. Here's a skeleton which assume you have a self.photos
dictionary with a text reference to an image file. The image thumbnail may or may not be loaded into a live dictionary; may or may not be in a filesystem cache; otherwise is fetched from an online store. It could use Core Data, but the key to smooth scrolling is that you don't wait around for the data wherever it comes from.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Photo Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
//identify the data you are after
id photo = [self.photos objectAtIndex:indexPath.row];
// Configure the cell based on photo id
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//move to an asynchronous thread to fetch your image data
UIImage* thumbnail = //get thumbnail from photo id dictionary (fastest)
if (!thumbnail) { //if it's not in the dictionary
thumbnail = //get it from the cache (slower)
// update the dictionary
if (!thumbnail) { //if it's not in the cache
thumbnail = //fetch it from the (online?) database (slowest)
// update cache and dictionary
}
}
}
if (thumbnail) {
dispatch_async(dispatch_get_main_queue(), ^{
//return to the main thread to update the UI
if ([[tableView indexPathsForVisibleRows] containsObject:indexPath]) {
//check that the relevant data is still required
UITableViewCell * correctCell = [self.tableView cellForRowAtIndexPath:indexPath];
//get the correct cell (it might have changed)
[[correctCell imageView] setImage:thumbnail];
[correctCell setNeedsLayout];
}
});
}
});
return cell;
}
如果您使用某种单例图像存储管理器,您会期望管理器处理缓存/数据库访问的细节,这简化了这个例子.
If you are using some kind of singleton image store manager, you would expect the manager to deal with the details of cache / database access, which simplifies this example.
这部分
UIImage* thumbnail = //get thumbnail from photo id dictionary (fastest)
if (!thumbnail) { //if it's not in the dictionary
thumbnail = //get it from the cache (slower)
// update the dictionary
if (!thumbnail) { //if it's not in the cache
thumbnail = //fetch it from the (online?) database (slowest)
// update cache and dictionary
}
}
将被替换为类似
UIImage* thumbnail = [[ImageManager singleton] getImage];
(您不会使用完成块,因为当您返回主队列时,您实际上在 GCD 中提供了一个完成块)
(you wouldn't use a completion block as you are effectively providing one in GCD when you return to the main queue)
这篇关于在 cellForRowAtIndexPath 从 CoreData 加载图像会减慢滚动速度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!