UITableView 复用 Cell 的两种方式区别

UITableView 复用 Cell 的两种方式区别及dequeueReusableCellWithIdentifier:forIndexPath:多做了什么?

复用 Cell 的方式

在 iOS 编程中,为了提高应用的性能,我们往往在动态显示表格数据时,复用 tableView 中的 Cell,这可以分为注册和不注册两种方式来实现,有关具体的使用和原理可以参考其他资料,这里重点讨论两种方式的区别。下面给出示例代码,以便讨论区别。

注册
- (void)viewDidLoad 
{
    [super viewDidLoad];
    // 如果使用 Nib 自定义 Cell
    [self.tableView registerNib:[UINib nibWithNibName:@"CustomCell" bundle:nil] forCellReuseIdentifier:@"myCell"];

    // 如果使用代码自定义 Cell
    [self.tableView registerClass:[CustomCell class] forCellReuseIdentifier:@"myCell"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    static NSString *identifier = @"mycell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier forIndexPath:indexPath];
    // Configure the cell...
    ...
    return cell;
}
不注册
- (void)viewDidLoad
{
    [super viewDidLoad];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{
    static NSString *identifier = @"mycell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }
    // Configure the cell...
    ...
    return cell;
}

区别

代码上的区别在于,注册的方法需要提前将要复用的 Cell 类注册,而不需要在获取 Cell 时手动判断 dequeue 的结果是否为nil,这是因为 dequeueReusableCellWithIdentifier:identifier forIndexPath:在内部处理了这个过程,使得最后返回的一定是可用的 Cell,参见头文件中的注释:

// Beginning in iOS 6, clients can register a nib or class for each cell.
// If all reuse identifiers are registered, use the newer -dequeueReusableCellWithIdentifier:forIndexPath: to guarantee that a cell instance is returned.
// Instances returned from the new dequeue method will also be properly sized when they are returned.
- (void)registerNib:(nullable UINib *)nib forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(5_0);

- (void)registerClass:(nullable Class)cellClass forCellReuseIdentifier:(NSString *)identifier NS_AVAILABLE_IOS(6_0);

我们可以注意到在获取 Cell 时,两种方式调用了不同的 method:

- (nullable __kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier;  // Used by the delegate to acquire an already allocated cell, in lieu of allocating a new one.

- (__kindof UITableViewCell *)dequeueReusableCellWithIdentifier:(NSString *)identifier forIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0); // newer dequeue method guarantees a cell is returned and resized properly, assuming identifier is registered

第一个 method 用在了非注册的方式里,第二个 method 用在了需要注册的方式里。经过验证,第一个 method 也可以用在需要注册的方式里,但是第二个 method 如果用于非注册的方式,则会报错崩溃:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'unable to dequeue a cell with identifier mycell - must register a nib or a class for the identifier or connect a prototype cell in a storyboard'

那么这两种 method 的实现有什么不同呢?为什么第二个 method 需要 indexPath?在大多数介绍复用 Cell 原理的资料中都没有涉及到这个问题。
在官方文档中,对于 indexPath 参数的描述为:

indexPath
The index path specifying the location of the cell. The data source receives this information when it is asked for the cell and should just pass it along. This method uses the index path to perform additional configuration based on the cell’s position in the table view.

indexPath 指定了 cell 的位置。 数据源会在被 cell 请求数据时收到此信息,并且会将其传递给它。 此方法会根据 cell 在 tableView 中的位置使用 indexPath 执行其他设置。

根据文档中的描述,方法会依据 cell 在 tableView 中的位置来对该 cell 进行一些配置。那么会做哪些事情呢?我在 stackoverflow 上获悉了这个问题:When to use dequeueReusableCellWithIdentifier vs dequeueReusableCellWithIdentifier : forIndexPath
其中高票回答正好提到了 indexPath 的作用:

Session 200 (- What’s New in Cocoa Touch from WWDC 2012) discusses the (then-new) forIndexPath: version starting around 8m30s. It says that “you will always get an initialized cell” (without mentioning that it will crash if you didn’t register a class or nib).

The video also says that “it will be the right size for that index path”. Presumably this means that it will set the cell’s size before returning it, by looking at the table view’s own width and calling your delegate’s tableView:heightForRowAtIndexPath: method (if defined). This is why it needs the index path.

由此可见,所谓 additional configuration 指的应该是依据 cell 位置和 tableView 的长宽属性对要返回的 cell 进行 size 配置。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值