扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
接上一篇翻译的内容。
创新互联建站长期为超过千家客户提供的网站建设服务,团队从业经验10年,关注不同地域、不同群体,并针对不同对象提供差异化的产品和服务;打造开放共赢平台,与合作伙伴共同营造健康的互联网生态环境。为黄陵企业提供专业的成都网站设计、网站制作,黄陵网站改版等技术服务。拥有10余年丰富建站经验和众多成功案例,为您定制开发。
再次运行测试
在重用cell之后,你可以再一次测试滚动性能。从表格3-3可以看出,在你正确重用cell之后,性能提升了一倍。
表格 3-3 重用cell之后测试的结果
结果显示你的做法是正确的;但是,现在的性能依然不够好。你总是想让性能上升至0.6666-0.0001左右;对于一个标准的UITableViewCell这是一个正常的性能,就像在第一部分展示的那样。所以下一部分将要学习如何重用p_w_picpaths,而不是每次调用的时候都创建一个新的p_w_picpath。
这就是你为什么要重用cell的原因。对于OS来说,创建和加载一个新的cell到内存中,都是要花费时间和内存资源的。这就是为什么tableview总是queue 一个cell进行重用,无论这个cell是否在屏幕之外。如果你重用cell,OS就不需要创建一个新的cell来显示;它只需要获取旧的cell,改变一些属性,然后重新显示这个cell。这个过程比OS创建一个新的cell要快的多。
重用图片
显示图片的主要问题在于加载的时间,要么通过文件系统IO,要么通过网络IO,都是非常耗时的。这个加载过程同样会影响到滚动性能,当iOS不能返回cell来渲染UI时。
由于这个原因,请参考ReuseImageViewController这个给工程。首先我解释一下在这些例子中,我为什么没有使用[UIImage p_w_picpathd:@""]。p_w_picpathNamed做了一项非常重要的工作:它会在内存中缓存图片,当你再次访问的时候会重用它。使用这个方法的问题是它只能从bundle中获取文件 --- 换句话说,图片只能和app的源代码放在一起。通常,你必须调用方法[UIImage alloc] initWithContentsOfFile:@""];或者[UIImage alloc] initWithData:data]]。调用这些方法,OS不会自动的在内存中缓存图片。
所以,我希望你通过一个小的dictionary在内存中存储图片,来自己缓存图片(请看第4章)。另外一个图片处理非常重要的部分是多线程(请看第6章)。使用这个技术,你可以把耗时的的处理任务放到当前线程之外。在我的当前例子中,不会使用多线程,因为你必须立即了解很多新的概念。在本章结束的时候,你应该自己做完这个练习。
这是在NSDictionary缓存图片的主要代码(请不要使用这种方式存储图片,因为它会导致内存警告)。
// Code to store the p_w_picpath in the dictionary- (UIImage *)p_w_picpathWithName:(NSString *)name {
if ([self.p_w_picpathDictionary objectForKey:name]) {return [self.p_w_picpathDictionary objectForKey:name];
}
UIImage *p_w_picpath = [[UIImage alloc] initWithContentsOfFile:name];[self.p_w_picpathDictionary setObject:p_w_picpath forKey:name];
return p_w_picpath;
}
下面这个是提取最近一次图片的主要代码。
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"CellIdentifier";
ReuseTableViewCell *cell = (ReuseTableViewCell *) [selfgetCellWithTableView:tableView cellIdentifier:CellIdentifier nibName:@"ReuseTableViewCell"];
NSString *avatarFile = [NSString stringWithFormat:@"a0"];
NSString *avatarName = [[NSBundle mainBundle] pathForResource:avatarFile ofType:@"jpeg"];
cell.avatar.p_w_picpath = [self p_w_picpathWithName:avatarName];
cell.userName.text = [NSString stringWithFormat:@"hi here: %d", indexPath.row];// Configure the cell.
return cell;
}
代码更新之后,你再运行一下测试。从表格3-4来看,你得到了一个更好的结果。现在的平均运行时间是0.002,fps的性能现在接近60。和之前的ReuseTableView相比,你得到了一个更好的性能。
表格 3-4 在重用图片之后的测试结果
好极了!fps现在几乎是60了,预加载的时间也降低了。如果你的apps能够达到这个水平,你不必再担心滚动时的性能了;它非常的流畅。通常,对于一个正常的,简单的里面包含很多subviews的UITableView来说,这已经是一个非常好的性能了。这样是非常好的,因为你不必在开始的时候就做很多工作。如果滚动的性能依然不好,你就必须使用一个更好但是更复杂的方式来达到同样的性能。
正如在第1章和第2章提到的,你应该总是小心谨慎,避免过度优化。为了一个很小的性能优化而浪费太多的时间是非常不值得的。因此从这点考虑,如果你的应用依然存在滚动性能方面的问题,你应该转到第2个例子,它使用到了UITableViewCell的绘制技术。
减少预加载时间
通常,我会通过缓存来重用图片,同时减少初始化的过程。当OS需要为TableView渲染一个新的cell,会通过调用下面的方法来返回一个新的cell:
- (UITableViewCell *)tableView:(UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Initialize and return the Cell here
}
因此,如果你在这里阻塞了太长的时间,UserInterface渲染的过程就会被阻塞;它将不能做任何事情或显示任何新的东西。这就是为什么用户看到在某个地方滚动停止的原因。
为了使这个过程尽可能的快,你可以去除一些逻辑,延迟计算,通过重用来缓存数据和图片。另外一个方法是通过首先使用默认的图片和数据来重用cell。当要获取图片或数据的时候,你可以使用多线程,然后稍后进行填充。从用户的角度来看,这种方法将会使得滚动更加流程,加载图片的速度更快。
第二个例子
当你有很多subviews或使用老的设备,绘制一个自定义的cell能够提升应用程序的性能。对于iPhone4和以后的设备,有一个非常显著的性能提升,因此你将会看到绘制自定义cell技术会有一个很大的不同。
在这个例子中,我会增加cells的复杂度,这是来自一个真实的应用,每一个cell有10个subviews,包括图片和文件。因此,你会看到很多真实的应用(像Facebook,这是我们尝试模拟的),滚动性能会被复杂的subviews结构严重影响到。我测试的应用有一个类似图 3-5这样的一个用户界面。
图 3-5 第二个应用例子
每一个cell包含一个头像,用户名,邮件的图片,标题和内容。它同时也显示了应用发送邮件的时间。测试结果图表格3-5。
表格3-6显示了运行自定义绘制代码的结果
从表格3-5和3-6可以看出,使用自定义绘制代码能够显著的提升渲染性能。在使用复杂的subview这点上,这个性能已经足够好了,你不必需要其他的优化了。
对于没有优化的cell,它将会创建很多的components和subviews。请查看图3-5,确保你理解了这个问题。
暂时翻译到这里,有时间继续!
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流