UITableView 的两种复用 cell 方法的区别

news/2024/7/20 21:41:51 标签: ios, objective-c, xcode

做过 iOS 开发的人都知道, iOS 的 UITableView 的 Cell 需要复用, 复用的时候有两种方法可以调用

  • dequeueReusableCell(withIdentifier:)
  • dequeueReusableCell(withIdentifier:for:)

那么他们到底有什么区别?

himg
himg

之前没有深究过这个问题, 每次用的时候只要使用了 register(_:forCellReuseIdentifier:) 就能保证 App 的效果绝对不会有问题. 前两天心血来潮, 总觉得他们一定有什么不同, 尽管他们只差了一个参数而已

从 Apple 的 Documentation 中是很难看不出区别的, 直到我看了 stackoverflow 上的答案. 区别有两点:

  1. 如果没有使用 register(_:forCellReuseIdentifier:)

    • dequeueReusableCell(withIdentifier:) : 会返回 nil
    • dequeueReusableCell(withIdentifier:for:) : 会直接 crash!
  2. dequeueReusableCell(withIdentifier:for:) 会在初始化时寻找代理方法 tableView(_:heightForRowAt:) 返回的高度, 从而使我们的cell 在 cell 初始化时就可以知道其本身的高度(有时这会非常有用).

得到答案之后, 我们再回头看 Apple 的文档

  • dequeueReusableCell(withIdentifier:)

    ... This method dequeues an existing cell if one is available or creates a new one using the class or nib file you previously registered. If no cell is available for reuse and you did not register a class or nib file, this method returns nil.

    If you registered a class for the specified identifier and a new cell must be created, this method initializes the cell by calling its init(style:reuseIdentifier:) method. For nib-based cells, this method loads the cell object from the provided nib file. If an existing cell was available for reuse, this method calls the cell's prepareForReuse() method instead.

  • dequeueReusableCell(withIdentifier:for:)

    Important: You must specify a cell with a matching identifier in your storyboard file. You may also register a class or nib file using the register(_:forCellReuseIdentifier:) method, but must do so before calling this method.

很明显, dequeueReusableCell(withIdentifier:for:) 的文档着重强调我们一定要在使用该方法前调用 register(_:forCellReuseIdentifier:), 为的就是防止 crash!

示例

我们可以像下面这样使用 dequeueReusableCell(withIdentifier:), 在没有 register 的情况下手动调用指定 UITableViewCell 的初始化方法:

class TestTableViewVCUITableViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

// MARK: - TableView Delegate & Data Source

extension TestTableViewVC {
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell"asTestTableViewCell ?? .init(style: .default, reuseIdentifier: "TableViewCell")
        cell.setContent(with: indexPath.row.description)
        return cell
    }
}

但是如果使用 dequeueReusableCell(withIdentifier:for:) 的话, 必须与 register(_:forCellReuseIdentifier:) 配合使用

class TestTableViewVCUITableViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(TestTableViewCell.self, forCellReuseIdentifier: "TableViewCell")
    }
}

// MARK: - TableView Delegate & Data Source

extension TestTableViewVC {
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell"for: indexPath) asTestTableViewCell
        cell.setContent(with: indexPath.row.description)
        return cell
    }
}

总结

啰嗦了那么多, 其实总结下来很简单:

  • 从初始化角度来看
    • 如果在调用两者之前已经使用了 register(_:forCellReuseIdentifier:), 两者没区别;
    • 如果在调用两者之前没有使用 register(_:forCellReuseIdentifier:), 那么 dequeueReusableCell(withIdentifier:) 还可以给你一次自我拯救的机会, 但是 dequeueReusableCell(withIdentifier:for:) 则会直接 crash, 不会给你任何机会
  • 从约束布局角度来看
    • dequeueReusableCell(withIdentifier:) 进行初始化时直接使用默认行高 44
    • dequeueReusableCell(withIdentifier:for:) 会在初始化时寻找代理方法 tableView(_:heightForRowAt:)(如果有的话) 返回的高度

在看到这个 stackoverflow 时, 一个回答说:

  • dequeueReusableCell(withIdentifier:): 在 tableView(_:cellForRowAt:) 方法返回 cell 后添加到 superview 中
  • dequeueReusableCell(withIdentifier:for:): 在 tableView(_:cellForRowAt:) 方法返回 cell 前添加到 superview 中

经过我的验证, 这是完全错误的, 两种方法都是在 tableView(_:cellForRowAt:) 返回 cell 后才会被添加到 superview 上, 可能在之前的某个系统版本上出现过这样的 bug, 但是现在这个 bug 已经被解决了

Ref

  • iOS: What is a difference between dequeueReusableCell(withIdentifier:for:) and dequeueReusableCell(withIdentifier:)?
  • iOS learning-the difference between the two reuse methods of UITableViewCell
  • dequeueReusableCellWithIdentifier:forIndexPath: VS dequeueReusableCellWithIdentifier:

http://www.niftyadmin.cn/n/824608.html

相关文章

口语练习Day1

方法就是重复跟读,背诵对话,乃至背诵短文!!! Part 1 Pronunciation Good morning honey. How’d you sleep?Slept like a log!That’s good. You are just in time for breakfast.Oh, What are we having?Same old…

口语练习Day2

Part 1 Pronunciation Jeff, time to wake up!Five more minutes! I’m so sleepy!Get up. I’ll open the curtains. Put on your clothes!Just let me sleep!Get up NOW! Your breakfast is getting cold, now come on! We waiting on you! Part 4 Key Points 叫起床&…

Apple 开发者账户类型

这里总结下 Apple 开发者账户的类型区别 himgiOS 开发账户等级 个人 费用: 99 美元 / 年创建 Apple ID: 需要App Store 上架: 是最大 udid 支持数: 每种设备各 100 台, 续费时可以从新编辑协作人数: 1 人 (开发者自己)该账号在 App Store 销售者只能显示个人的 ID公司 费用: 99…

python提取并修改VOC数据集中xml格式文件的bounding box坐标

下面一段代码是VOC数据集中的一个xml格式的文件&#xff0c;其包含两个person的目标信息&#xff1a; <annotation><folder>JPEGImages</folder><filename>00002.jpg</filename><path>/media/mts/Document/Datasets/VOC2007/JPEGImages/0…

2021 年度总结

2021 -- 生活逐渐稳定, 工作跌宕起伏 himg2021 年, 对比 2020 年, 我今年的变化主要体现在技术经验的积累, 基础 (算法 & 数据结构) 的强化, 以及收入的增长 工作上 从 2020 年我来深圳入职公司 Z 后, 一致兢兢业业, 认真负责, 保证让我负责的工作按时保质保量交付. 入职时…

数理统计完全教程作业1

2.3. 引理证明&#xff1a; P(Xx)F(x)−F(x−)&#xff0c;其中&#xff0c;F(x−)limy→xF(y).\mathbb{P}(Xx)F(x)-F(x^{-})&#xff0c;其中&#xff0c;F(x^{-})\mathop{\text{lim}}\limits_{y \to x}F(y).P(Xx)F(x)−F(x−)&#xff0c;其中&#xff0c;F(x−)y→xlim​F(y…

什么是迁移学习

引言 跟传统的监督式机器学习算法相比&#xff0c;深度神经网络目前最大的劣势是什么&#xff1f; 贵。 尤其是当我们在尝试处理现实生活中诸如图像识别、声音辨识等实际问题的时候。一旦你的模型中包含一些隐藏层时&#xff0c;增添多一层隐藏层将会花费巨大的计算资源。 …

激活函数ReLU、Leaky ReLU、PReLU和RReLU

激活函数”能分成两类——“饱和激活函数”和“非饱和激活函数”。 sigmoid和tanh是“饱和激活函数”&#xff0c;而ReLU及其变体则是“非饱和激活函数”。使用“非饱和激活函数”的优势在于两点&#xff1a; 1.首先&#xff0c;“非饱和激活函数”能解决所谓的“梯度消失”…