我正在构建一个类似于iPod.app的专辑浏览视图的UITableView:
首次发布时,我正在从iPod库导入所有艺术家和专辑作品。将所有内容保存到CoreData并将其恢复为NSFetchedResultsController。我正在重复使用单元格标识符,在我的cellForRowAtIndexPath:
方法中我有这个代码:
Artist *artist = [fetchedResultsController objectAtIndexPath:indexPath];
NSString *identifier = @"bigCell";
SWArtistViewCell *cell = (SWArtistViewCell*)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil)
cell = [[[SWArtistViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
cell.artistName = artist.artist_name;
cell.artworkImage = [UIImage imageWithData:artist.image];
[cell setNeedsDisplay];
return cell;
我的SWArtistViewCell单元格实现了drawRect:
方法来绘制字符串和图像:
[artworkImage drawInRect:CGRectMake(0,1,44,44)]
[artistName drawAtPoint:CGPointMake(54, 13) forWidth:200 withFont:[UIFont boldSystemFontOfSize:20] lineBreakMode:UILineBreakModeClip];
滚动仍然不稳定,我无法弄清楚原因。像iPod和Twitter这样的应用程序有黄油平滑滚动,但他们都像我一样在单元格中绘制一些小图像。
我的所有观点都是不透明的。我错过了什么?
编辑:以下是Shark所说的内容:
我不熟悉鲨鱼。任何指针与这些符号有什么关系?当我看到这些的痕迹时,它们都指向我的drawRect:方法,特别是UIImage绘图。
如果扼流圈是文件读数,它会指向别的东西吗?这绝对是绘画吗?
编辑:保留图片
我已经按照pothibo的建议完成了,并将一个artworkImage方法添加到我的Artist类中,该类保留了使用imageWithData创建的图像:
- (UIImage*)artworkImage {
if(artworkImage == nil)
artworkImage = [[UIImage imageWithData:self.image] retain];
return artworkImage;
}
所以现在我可以直接将保留的图像设置为我的TableViewCell,如下所示:
cell.artworkImage = artist.artworkImage;
我还在我的tableViewCell类的setArtworkImage:方法中设置了setNeedsDisplay。滚动仍然是滞后的,Shark显示完全相同的结果。
答案 0 :(得分:6)
您的分析数据强烈暗示了解压缩PNG图像的瓶颈。我的猜测是,你提供的CPU时间的58.5%用于解压缩PNG数据(即如果memcpy调用也包含在加载中)。可能更多的时间花在那里,但没有更多的数据很难说。我的建议:
将图像加载到工作线程中,不会影响主线程的响应能力(同样多)。创建一名工人非常容易:
[self performSelectorOnMainThread:@selector(preloadThreadEntry :) withObject:nil waitUntilDone:NO];
将图像预加载到前方,例如100行或更多(在您滚动的方向上为70,在相反方向保持30)。如果您的所有图像在视网膜上需要88x88像素,则100张图像不需要超过2 MB。
只有在此之后仍然存在性能问题时,我建议您研究缩放,例如为视网膜加载“... @ 2x.png”图像。祝你好运!
答案 1 :(得分:4)
[UIImage imageWithData:]不缓存。
这意味着每次传入dataSource方法时,CoreGraphic都会解压缩并处理您的图像。
我会改变你的艺术家的对象来保持UIImage而不是NSData。你可以随时刷新memoryWarning上的图像,如果你得到了很多。
另外,我不建议在dataSource调用中使用setNeedsDisplay,我会在你的单元格中使用它。
SetNeedsDisplay不是对drawRect的直接调用:
它只告诉操作系统在runloop结束时再次绘制UIVIew。您可以在同一个runloop中调用setNeedsDisplay 100次,操作系统只会调用一次drawRect方法。
答案 2 :(得分:1)
此时最好的办法是使用Shark尝试查找代码中的瓶颈。 Apple有一个“Performance Tuning Your Application with Shark”(iTunes链接)视频,如果您以前从未使用过它,可以引导您浏览Shark。我认为你必须是99美元iPhone开发者计划的成员才能查看它。
答案 3 :(得分:1)
我的猜测是延迟是在Core Data中存储图像。核心数据通常不是存储大量数据的好方法。
更好的解决方案是将图像作为单个文件存储在磁盘上,使用相册ID识别每个图像。然后,您将设置内存缓存以将图像存储在RAM中,以便快速加载到UIImageView
中。理想情况下,需要在后台线程上完成从磁盘到RAM的映像加载(例如,尝试performSelectorOnBackgroundThread),这样I / O就不会陷入主线程(这将影响滚动性能)。
如果你每个单元加载时只调用-drawRect:
一次(就像你应该的那样),那么问题可能就是图像的缩放。通过使用drawInRect
在代码中绘制图像来缩放图像将使用CPU时间,因此另一种方法是在从iPod库接收图像时缩放图像(如果您需要多种尺寸的图像,请在每个图像中保存一个版本你需要的尺寸)。您可能需要在导入数据时在后台线程上执行此操作,以避免阻止主(UI)线程。
另一个需要考虑的事情是UIImageView
可以使用Core Animation进行缩放,这意味着它是硬件加速的(我不确定这是否真的如此,我只是在猜测)。因此,切换到图像的UIImageView
将消除图像缩放的CPU负担。合成开销略有增加,但它可能是接近“最佳”滚动性能的最简单方法。
答案 4 :(得分:1)
如果您的-drawRect
发生了延迟,那么您可能需要查看this article:Tweetie的开发人员非常彻底地解释了他用来获得平滑滚动的方法。从那时起,这变得有点容易了:CALayer
有一个shouldRasterize
属性,基本上将其子图层变平为一个位图,然后只要图层内部没有任何变化就可以让你更好在动画图层时的性能,如UITableView
滚动时所做的那样。在这种情况下,您可能会将该属性应用于各个UITableViewCell
s'层。