UITableView性能提升和优化(第

发布时间:2019-09-02 07:39:57编辑:auto阅读(1703)

    接上一篇翻译的内容。


    再次运行测试


    在重用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,确保你理解了这个问题。




    暂时翻译到这里,有时间继续!


关键字