UITableViewCell复用机制及踩坑总结

最近在项目开发中遇到了一些关于UITableViewCell的问题,当我在反复滑动tableView的时候,cell上的内容出现了变更,同时我最近也恰好在掘金上看到了另一篇关于这个问题的文章,参考了一下并稍做深刻的研究了一下UITableViewCell的复用机制,若有问题还请指正,一块儿提升。swift

关于这个复用机制,网上众说纷纭,其中大部分的说法都是先生成能覆盖一屏幕的cell,而后上下滑动的时候再把这些cell复用,好比我向下滑的时候最上面的cell移除了视图,那么就把他复用为最下面的cell。这样只需生成略多于一屏幕的cell数量就能实现功能,还节省资源。bash

不过UITableView毕竟没有开源,具体怎么实现的也不清楚,因此搞了一个小小的实验,直接贴一段代码。
编译环境:Xcode11.3.一、iOS13.2ide

//TableViewController.swift
class TableViewController: UITableViewController {
    
    let dataSourse = ["","","","","test","test","test","test","test","test","test","test"]

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(TestTableViewCell.self, forCellReuseIdentifier: TestTableViewCell.reuseIdentifier)
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        var cell = tableView.dequeueReusableCell(withIdentifier: TestTableViewCell.reuseIdentifier) as? TestTableViewCell

        print("--------cell" + String(indexPath.row) + "被添加到视图中了")

        cell!.data = dataSourse[indexPath.row]
        
        print(cell!.description)
        
        return cell!
    }
    
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 100
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return dataSourse.count
    }
}
复制代码
//TestTableViewCell.swift
class TestTableViewCell: UITableViewCell {
    
    static let reuseIdentifier = "TestTableViewCell"
    
    let indicateLabel = UILabel()
    let dataLabel = UILabel()
    
    var data: String! {
        didSet {
            display()
        }
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
        
        indicateLabel.frame = CGRect(x: 0, y: 30, width: 100, height: 40)
        dataLabel.frame = CGRect(x: 100, y: 30, width: 100, height: 40)
        
        contentView.addSubview(indicateLabel)
        contentView.addSubview(dataLabel)
    }
    
    func display() {
        
        //需注意的是在data为空的状况下并无对dataLabel进行赋值
        if data == "" {
            indicateLabel.text = "暂无数据"
        } else {
            indicateLabel.text = "有数据"
            dataLabel.text = data
        }
    }

}
复制代码

打印结果截图以下。post

我发现全部的12个cell都生成了,我特意把高度拉的很高便于测试,事实上图中仅能看见8-9个cell。测试

这说明了在tableView调用该数据源方法时是将全部cell所有加载好了,对于这个打印结果,我其实也半信半疑,多是恰好某种状况让我撞见了?因此还请各位大神们指点一二(手动抱拳ui

那么随着我向下拉视图,能够发现cell的复用情况以下:spa

九、十、11的第一次出现是承接了上图,那么九、十、11的第二次出现即是下拉后的输出结果,能够明显的发现,这个新的9的cell是和第一次的11的地址是同样的,就能够理解为向下滑露出的第一个cell的内容是复用自最初加载的全部的cell的最后一个,由于此时最上面的cell还不必定消失,暂时不能被复用。那么新的十、11就和上图中的0、1对应,是复用自他们的,能够保证最上面的cell已经消失,再也不可以被看到。3d

此时再向上滑动,能够发现复用情况更新以下:code

很好理解,从下向上二、一、0按顺序出如今屏幕上,对照上面两图能够发现,这个2是没被动过的,而一、0是被十一、10复用过的,那么就出现了一个一开始看会以为很离谱的状况:cdn

明明原本没有数据的,为何又有了呢?关于这种状况出现的缘由和解决方案,能够看这篇文章,也是最近发布的一篇,大意为复用的cell的内容设置不会更改,当我没有显式的指定某个控件的内容,再进行复用就容易出问题。本例中十一、10地址的cell的右侧label值被设置为了test,而再复用到一、0中时因为代码并无显式的给右侧label赋值(无数据),就出现了这种结果。那篇文章提供了几个解决方案及其优劣讨论,我想补充一点的就是cell不管处于哪一种case均可以显式的给控件上一个值,这样就避免了这种问题的出现。

ok,踩了个小坑,也探究了cell复用究竟是怎么回事。对于这个cell复用,我仍是对个人实验结果不够自信。。。分享一下,但愿大佬们多提宝贵意见。


更新一下,当我把cell数量增到1000的时候,发现仅仅打印到cell18,由此能够推测出以前并非把全部的cell所有加载好放在内存中,复用池数量维持在当前屏幕可展现数量的两倍的状态,若是cell总数量小于复用池数量的话就所有加载。接下来的过程和以前同样,若是下拉的话是先把复用池中最后一个取出拿来复用,也就是18,而后是0、一、2这样。

相关文章
相关标签/搜索