• iOS开发UI篇—UITableviewcell的性能优化和缓存机制


    iOS开发UI篇—UITableviewcell的性能问题

    一、UITableviewcell的一些介绍

    UITableView的每一行都是一个UITableViewCell,通过dataSource的 tableView:cellForRowAtIndexPath:方法来初始化每⼀行

    UITableViewCell内部有个默认的子视图:contentView,contentView是UITableViewCell所显示内容的父视图,可显示一些辅助指示视图

    辅助指示视图的作⽤是显示一个表示动作的图标,可以通过设置UITableViewCell的 accessoryType来显示,默认是UITableViewCellAccessoryNone(不显⽰示辅助指⽰示视图), 其他值如下:

    UITableViewCellAccessoryDisclosureIndicator

    UITableViewCellAccessoryDetailDisclosureButton

    UITableViewCellAccessoryCheckmark

    还可以通过cell的accessoryView属性来自定义辅助指示视图(⽐如往右边放一个开关) 

    二、问题

      cell的工作:在程序执行的时候,能看到多少条,它就创建多少条数据,如果视图滚动那么再创建新显示的内容。(系统自动调用)。即当一个cell出现在视野范围内的时候,就会调用创建一个cell。这样的逻辑看上去没有什么问题,但是真的没有任何问题吗?

      当创建调用的时候,我们使用nslog打印消息,并打印创建的cell的地址。我们发现如果数据量非常大,用户在短时间内来回滚动的话,那么会创建大量的cell,一直开辟空间,且如果是往回滚,通过打印地址,我们会发现它并没有重用之前已经创建的cell,而是重新创建,开辟新的存储空间。

      那有没有什么好的解决办法呢?

    三、cell的重用原理

    (1) iOS设备的内存有限,如果用UITableView显示成千上万条数据,就需要成千上万 个UITableViewCell对象的话,那将会耗尽iOS设备的内存。要解决该问题,需要重用UITableViewCell对象

    (2)重⽤原理:当滚动列表时,部分UITableViewCell会移出窗口,UITableView会将窗口外的UITableViewCell放入一个对象池中,等待重用。当UITableView要求dataSource返回 UITableViewCell时,dataSource会先查看这个对象池,如果池中有未使用的UITableViewCell,dataSource则会用新的数据来配置这个UITableViewCell,然后返回给 UITableView,重新显示到窗口中,从而避免创建新对象 。这样可以让创建的cell的数量维持在很低的水平,如果一个窗口中只能显示5个cell,那么cell重用之后,只需要创建6个cell就够了。

    (3)注意点:还有⼀个非常重要的问题:有时候需要自定义UITableViewCell(用⼀个子类继 承UITableViewCell),而且每⼀行⽤的不一定是同一种UITableViewCell,所以一 个UITableView可能拥有不同类型的UITableViewCell,对象池中也会有很多不同类型的 UITableViewCell,那么UITableView在重⽤用UITableViewCell时可能会得到错误类型的 UITableViewCell

    解决⽅方案:UITableViewCell有个NSString *reuseIdentifier属性,可以在初始化UITableViewCell的时候传入一个特定的字符串标识来设置reuseIdentifier(一般用UITableViewCell的类名)。当UITableView要求dataSource返回UITableViewCell时,先 通过一个字符串标识到对象池中查找对应类型的UITableViewCell对象,如果有,就重用,如果没有,就传入这个字符串标识来初始化⼀一个UITableViewCell对象。

    图片示例:

    说明:一个窗口放得下(可视)三个cell,整个程序只需要创建4个该类型的cell即可。

    四、cell的优化代码

     代码示例:

    复制代码
     1 #import "NJViewController.h"
     2 #import "NJHero.h"
     3 
     4 // #define ID @"ABC"
     5 
     6 @interface NJViewController ()<UITableViewDataSource, UITableViewDelegate>
     7 /**
     8  *  保存所有的英雄数据
     9  */
    10 @property (nonatomic, strong) NSArray *heros;
    11 @property (weak, nonatomic) IBOutlet UITableView *tableView;
    12 
    13 @end
    14 
    15 @implementation NJViewController
    16 
    17 #pragma mark - 懒加载
    18 - (NSArray *)heros
    19 {
    20     if (_heros == nil) {
    21         // 1.获得全路径
    22         NSString *fullPath =  [[NSBundle mainBundle] pathForResource:@"heros" ofType:@"plist"];
    23         // 2.更具全路径加载数据
    24         NSArray *dictArray = [NSArray arrayWithContentsOfFile:fullPath];
    25         // 3.字典转模型
    26         NSMutableArray *models = [NSMutableArray arrayWithCapacity:dictArray.count];
    27         for (NSDictionary *dict in dictArray) {
    28             NJHero *hero = [NJHero heroWithDict:dict];
    29             [models addObject:hero];
    30         }
    31         // 4.赋值数据
    32         _heros = [models copy];
    33     }
    34     // 4.返回数据
    35     return _heros;
    36 }
    37 
    38 - (void)viewDidLoad
    39 {
    40     [super viewDidLoad];
    41     // 设置Cell的高度
    42     // 当每一行的cell高度一致的时候使用属性设置cell的高度
    43     self.tableView.rowHeight = 160;
    44 }
    45 
    46 #pragma mark - UITableViewDataSource
    47 // 返回多少组
    48 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
    49 {
    50     return 1;
    51 }
    52 // 返回每一组有多少行
    53 - (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    54 {
    55     return self.heros.count;
    56 }
    57 // 当一个cell出现视野范围内的时候就会调用
    58 // 返回哪一组的哪一行显示什么内容
    59 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    60 {
    61     // 定义变量保存重用标记的值
    62     static NSString *identifier = @"hero";
    63     
    64 //    1.先去缓存池中查找是否有满足条件的Cell
    65     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    66 //    2.如果缓存池中没有符合条件的cell,就自己创建一个Cell
    67     if (cell == nil) {
    68         //    3.创建Cell, 并且设置一个唯一的标记
    69         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
    70         NSLog(@"创建一个新的Cell");
    71     }
    72 //    4.给cell设置数据
    73     NJHero *hero = self.heros[indexPath.row];
    74     cell.textLabel.text = hero.name;
    75     cell.detailTextLabel.text = hero.intro;
    76     cell.imageView.image = [UIImage imageNamed:hero.icon];
    77     
    78    //  NSLog(@"%@ - %d - %p", hero.name, indexPath.row, cell);
    79     
    80     // 3.返回cell
    81     return cell;
    82 }
    83 
    84 #pragma mark - 控制状态栏是否显示
    85 /**
    86  *   返回YES代表隐藏状态栏, NO相反
    87  */
    88 - (BOOL)prefersStatusBarHidden
    89 {
    90     return YES;
    91 }
    92 @end
    复制代码

    缓存优化的思路:

    (1)先去缓存池中查找是否有满足条件的cell,若有那就直接拿来

    (2)若没有,就自己创建一个cell

    (3)创建cell,并且设置一个唯一的标记(把属于“”的给盖个章)

    (4)给cell设置数据

    注意点:

    定义变量用来保存重用标记的值,这里不推荐使用宏(#define来处理),因为该变量只在这个作用域的内部使用,且如果使用宏定义的话,定义和使用位置太分散,不利于阅读程序。由于其值不变,没有必要每次都开辟一次,所以用static定义为一个静态变量。

     
     
  • 相关阅读:
    OBS推流工具
    Spring学习笔记(二)
    设计模式
    力扣刷题 二分法 leetcode 刷题 27,26,283,844,977
    论文笔记 Processing Private Queries over Untrusted Data Cloud through Privacy Homomorphism 通过隐私同态处理不可信数据云上的私有查询
    今日链表操作leetcode 24 交换链表中的相邻两个结点
    leetcode刷题 leetcode22中等题 有效括号生成
    npm镜像 yarn镜像 homebrew镜像 各种镜像
    mac m1 pro 搭建php环境
    华为弹性ecs(centOS7)环境构建笔记
  • 原文地址:https://www.cnblogs.com/187n/p/5071537.html
Copyright © 2020-2023  润新知