您好,登录后才能下订单哦!
在本章,你将做下面这些事情:
在实例中使用第2章介绍的测试工具
在例子中一步一步的对滚动性能进行优化
使用以下技术对UITableView进行优化
1) 使用基本的技术优化UITableView中简单的cells
2) 通过代码使用核心技术在cell中绘制view
3) 使用基本的技术来优化需要像正在编辑,重排序等动画的cell
4) 开发者需要知道的一些其他技术
iPhone应用程序通常通过列表的形式来显示数据。苹果为开发者提供了非常好用的工具:UITableView 和 UITableViewCell。如果开发者只是想使用一些基本的功能,如在左边显示一张小图片,中间显示文本,那么苹果提供的默认控制就能够很好的满足要求了。但是如果你想自定义一些东西,比如显示2张或3张图片,把文本放在不同的地方,你就会遇到问题。如果这样的话,你迟早会遇到UITableView性能方面的一些问题,尤其是在像iPhone 3G这种老设备上。
例子介绍
在这个例子中,我会基于两个主要因素来衡量性能:UITableView dequeue一个cell,创建一个cell的速度,或cell返回给操作系统的速度;操作系统渲染你的cell,然后显示在机器上的速度。第一个使用NSLog就可以测量出来;第二个比较复杂,只能通过CoreAnimation进行测量。
我将用两个不同的例子来说明问题。一个只包含了图片和文字;另一个包含了很多复杂的子view。通过这两个例子,你会发现有很多不同的方法来优化UITableView滚动时的性能。
在本章结束时,我会列出很多重要的知识点,由于时间有限,我不会做详细的介绍。这些并不是一些常见的错误,但是如果某个开发者因为粗心而犯了其中的一些错误,有可能花上一整天的时间测试和查找问题。我想确保你已经有足够的技能和知识来处理各种情况。
有时候优化非常简单,只需要在代码中做一些小小的改动。然而在其他情况下,比如第二个例子,你需要重写整个代码来,从而达到更好的性能。我希望在例子介绍完后,你对整个程序的架构有一个非常清晰的认识,这样你在开始的时候就能够做出正确的决定,而不需要重写代码。
复习测试工具
在本章,你将使用CoreAnimation工具对iphone OS的渲染性能进行测试。这能帮助你了解问题是出现在计算过程中还是在显示过程中。第2章已经介绍了这个工具,所以本章只进行一个简短的回顾。
图 3-1 显示了CoreAnimation工具的主视图,运行时有3个部分你需要观察,图3-2显示了性能的参数值。
图 3-1 CoreAnimation 工具的主要部分
图 3-2 最近的性能显示
第一个例子
第一个例子将会一步一步的展示如何优化UITableView的滚动性能。最初版本的源代码包含了我从很多开发者那边搜集到的性能使用上的错误。在这个过程中,你会看到在每一步优化后,性能都会有所提升。
介绍第一个例子
如图3-3,你有一个普遍而又实际的问题,那就是你需要开发一个UITableView,在每一个cell中有一张图片和一个文本块。我将带你查看这个例子的源代码。让我们看一下类似Facebook的应用;应用需要一张图片显示头像,需要另一张图片显示用户分享的链接内容。这个应用同样也需要一张更小的图片来显示cell中的图标。在第一个测试中,请参考SlowPerformanceTableView这个工程。
图 3-3 第一个例子的应用
标准测试
在项目开始之前,你必须知道你的最终目标;在这个例子中,你要达到的目标就是有一个很好的性能,当在滚动和使用你的应用时,你可以带给用户一个很好的体验。因此,通过运行一个正常的没有自定义的UITableViewCell,它会按需加载一张简单的图片并会对图片进行重用,表格 3-1显示了运行时的日志。
表格 3-1 运行例子的结果
用CoreAnimation进行测试,每秒渲染的帧数(fps)的最佳性能是60fps(数字越高,性能越好)。对于一个标准的UITableViewCell,通常的速度在 55-60fps之间;这应该是你的目标之一。另一个目标是确保预加载的时间足够的小。当总体时间减少了,cell预加载的时间同样会减少。但是,减少cell预加载的时间更简单,因此在这个例子中,主要集中在如何减少预加载的时间。
初始化测试
在第一个例子中,我运行了一个初始化的测试,得到了6个cell滚动时的随机结果。表格3-2显示了测试性能的结果(使用了NSLog和CoreAnimation)。
表格3-2 第一个例子初始化测试的结果
从结果中可以看出,绘制和返回一个cell通常需要10毫秒的时间。由于这个时间是非常巨大的,通常的测量(fps)同样也会降低效率。因此,我的第一个目标就是减少这个过程的预加载时间。
源代码中有一个需要注意的地方:[UIImage p_w_picpathNamed:name]和[[UIImage alloc]initWithContentsOfFile:name] 之间的区别。稍后我会解释他们的不同以及为什么用 p_w_picpathNamed代替initWithContentsOfFile。
重用UITableViewCell
优化UITableView通常是非常简单的;你需要做的就是检查是否用正确的方式重用UITableViewCell。创建一个UITableViewCell对于iOS来说是一个CPU密集型的过程。因此,如果用户上下滚动的时候每次都要创建一个新的cell,整个性能将会下降。苹果的标准(默认)的方式就是:无论cell是否在屏幕之外,都重用这个cell,从而加速这个过程。
标准的UITableViewCell
对于标准的UITableViewCell,通用的代码实际上可以工作的很好,能够给你一个快速的滚动性能。
- (UITableViewCell *)tableView:(UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
注意reuseIdentifier:CellIdentifier anddequeueReusableCellWithIdentifier:CellIdentifier的使用。这两部分会帮助你正确的重用UITableViewCell。
有两种主要的方法创建一个自定义的cell,要么使用InterfaceBuilder,要么通过调用addSubview:方法,然后自己写代码来创建。
使用 InterfaceBuilder创建自定义UITableView
当使用InterfaceBuilder的时候,开发者通常会忘记设置identifier,实际上这个设置是非常简单的。可以通过打开xib文件,转到第一个tab,对第一行进行修改来改变identifier的值,如图3-4。
图3-4 为cell设置重用的identifier
现在,在cell的初始化代码中,你必须明确的使用相同的identifier。
- (UITableViewCell *)tableView:(UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"CellIdentifier"; // must match the one inInterfaceBuilder
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefaultreuseIdentifier:CellIdentifier];
}
通过代码自定义UITableView
如果你不想用interfacebuilder,而是用代码构建自定义的cell,你可以在你自定义的类ReuseTableView中返回它。
TableCellViewController.h
@interface TableCellViewController : UITableViewCell
{
}
@end
TableCellViewController.m
#import "TableCellViewController.h"
@implementation TableCellViewController
- (NSString *)reuseIdentifier
{
return @”CellIdentifier”;
}
@end
注意:这两种方法的主要不同之处在于加载和初始化你的UITableView。确保你能够理解这两个例子的不同之处,我将会向你展示主要的代码。 |
从Nib文件加载Cell
首先,你需要用代码从文件系统中加载nib文件到内存中,然后解析出给UITableView对象。
- (UITableViewCell *)cellWithTableView:(UITableView *)tableView cellIdentifier:(NSString*)cellIdentifier nibName:(NSString *)nibName {
UITableViewCell *textCell = [tableViewdequeueReusableCellWithIdentifier:cellIdentifier];
if (textCell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:nibName
owner:niloptions:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[UITableViewCell class]]) {
textCell = (UITableViewCell *)currentObject;
break;
}
}
}
return textCell;
}
你可以使用以下代码,调用cellWithTableView:cellIdentifier:nibName: 从nib TableViewController加载 stable view :
ReuseTableViewCell *cell = (ReuseTableViewCell *) [selfgetCellWithTableView:tableView
cellIdentifier:CellIdentifiernibName:@"ReuseTableViewCell"];
这个代码不是完美的;你需要修改成你自己的nib文件确保一切运行顺利。但是,我的目的是确保你能够区分这两种方式,所以我没有讨论过多的细节。
从自己的代码中加载cell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self != nil) {
UIImageView *p_w_picpathView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20,30, 30)];
[self.contentView addSubview:p_w_picpathView];}
return self;
}
给你一个小的练习,创建一个新的工程,尝试创建一个自定义的cell;你必须检查所有的代码确保你能够正确的重用cell。
由于篇幅有限,今天暂时介绍到这里,明天继续往下翻译。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。