UITableview是大家常用的UIKit组件之一,使用中我们最常遇到的就是对delegate和dataSource这两个委托的使用。我们大多数人可能知道当reloadData这个方法被调用时,delegate和dataSource就会被回调,但是其中具体的细节,可能很多人不会去探究。
我最近有兴趣来探讨这个问题是因为我最近遇到过dataSource中有的方法被调用,但是有的方法没有被调用的情况,同时你会发现当tableview被add到一个superView的时候,也会触发了reloadData一样的回调。那么这两个委托究竟是怎么执行的呢?

  • 我们首先来看看苹果文档对reloadData的描述

    Call this method to reload all the data that is used to construct the table,
    including cells, section headers and footers, index arrays, and so on. For
    efficiency, the table view redisplays only those rows that are visible. It adjusts
    offsets if the table shrinks as a result of the reload. The table view’s delegate or
    data source calls this method when it wants the table view to completely reload
    its data. It should not be called in the methods that insert or delete rows,
    especially within an animation block implemented with calls to beginUpdates and
    endUpdates.

    大致的意思就是说reload这个方法是用来构建table的,包括cell、section,而且只会对可见的行进行重新的绘制,当tableview想要完整的加载数据时,delegate和data source会调用此方法。增加删除行,尤其是需要block动画的时候不用用它。
    从这里只能看出个大概,并没有解释调用的原理。

  • 那么让我们先写一个最基本的tableview实现,然后对delegate和data source的回调设置一下断点看看。

- (void)viewDidLoad {
[super viewDidLoad]; UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(, , self.view.bounds.size.width, self.view.bounds.size.height)];
tableView.delegate = self;
tableView.dataSource = self; [self.view addSubview:tableView];
// [tableView reloadData];
// [tableView layoutSubviews];
} -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return ;
} -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return ;
} -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return ;
} -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *cellID = @"cellID";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
if(!cell){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID];
}
cell.textLabel.text = @"哈哈";
return cell;
}
我们先对下面这四个方法设置一下断点,然后观察左边的栈信息。

首先被调用的是numberOfSectionsInTableView:

numberOfSectionsInTableView:调用栈信息.png

我们可以看到从addSubview是如何一步步调用到numberOfSectionsInTableView:的。
好的,我们下一步看断点断到了tableView:numberOfRowsInSection:上。

tableView:numberOfRowsInSection:调用栈信息.png

_rebuildGeometry这个私有方法之前都是一样的,所以我这里并没有全截,可以看到_rebuildGeometry中不仅调用了_updateRowData,还调用了一个_updateContentSize,从这里来获得每个section的行数。
我们接着往下看,到了tableView:heightForRowAtIndexPath:

tableView:heightForRowAtIndexPath:调用栈信息.png

这里通过了一个block回调的方式获取了各个row的高度,并决定了整个section的高度。
然后我们会发现,以上的几个方法还会再被调用一遍:

numberOfSectionsInTableView:调用栈信息2.png

但是栈信息已经不一样了,这次调用时由于tableview调用了layoutSubviews,而reloadData是layoutSubviews里调用的一个方法,因为layoutSubviews也是个公有的方法,所以我们可以用它来触发reloadData。
断点继续执行,就执行到了tableView: cellForRowAtIndexPath:,我们用它来获取tableview每个row的cell。

tableView: cellForRowAtIndexPath:调用栈信息.png

我们会发现tableView: cellForRowAtIndexPath:并不是靠_rebuildGeometry下面的方法来触发,而只是靠layoutSubviews来触发,如果layoutSubviews没有执行成功,那么就可能会遇到我之前遇到过的前几个方法执行而tableView: cellForRowAtIndexPath:不执行的问题。

    • 多了解UIKit的栈信息能够帮我们了解苹果运行的机制和原理,从而帮我们解决一些看起来非常诡异的bug,多看看苹果的私有方法也有助于我们养成良好的编程习惯,我们尽量模仿苹果的代码规范无论是对自己写代码看着舒服,还是对他人来读我们写的代码都一件好事。

UITableview delegate dataSource调用探究的更多相关文章

  1. Swift_TableView(delegate,dataSource,prefetchDataSource 详解)

    Swift_TableView(delegate,dataSource,prefetchDataSource 详解) GitHub import UIKit let identifier = &quo ...

  2. C# Delegate 异步调用

    namespace ConsoleApplication22 { /// /// 异步操作 /// /// /// /// //internal Func<int,int,int> int ...

  3. UITableView 接口的调用顺序

    ios7启用estimatedHeightForRowAtIndexPath之后的api调用顺序called -[XHYTableViewController tableView:heightForR ...

  4. 快速设置UITableView不同section对应于不同种类的cell

    快速设置UITableView不同section对应于不同种类的cell 本文主要是为了写明如何在UITableView中,一个section对应于一种类型的cell,写起来不凌乱. 在不封装任何类的 ...

  5. 解决tableView中cell动态加载控件的重用问题

    解决tableView中cell动态加载控件的重用问题 tableView的cell,有时候需要在运行时取得对应的数据后才能够动态的创建该cell中的控件并加载到该cell中,此时,你一定会遇到重用问 ...

  6. 更新tableView的某个cell

    更新tableView的某个cell 异步加载完数据后更新某个cell,这应该是非常常见的使用方法了,我们经常会用reloadData. 效果: 源码: // // RootViewControlle ...

  7. iOS基础 - UITableView的数据源(dataSource)和代理(delegate)

    UITableView的数据源(dataSource)和代理(delegate) UITableView需要一个数据源(dataSource)来显示数据,UITableView会向数据源查询一共有多少 ...

  8. 教你写能被舒服舒服又舒服地调用的iOS库

    目录 前言 脑洞开一开 分析 整容 结语 前言 2014年过的那么快,过年又那么块,2015年又是飞快地节奏,真尼玛感觉上帝是不是无聊使用了变速外挂开启了加速模式~到现在博主都无法接受已经上班的事实… ...

  9. iOS应用架构谈(三):View层的组织和调用方案(下)

    iOS客户端应用架构看似简单,但实际上要考虑的事情不少.本文作者将以系列文章的形式来回答iOS应用架构中的种种问题,本文是其中的第二篇,主要讲View层的组织和调用方案.下篇主要讨论做View层架构的 ...

随机推荐

  1. 我的MYSQL学习心得(二) 数据类型宽度

    我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...

  2. 快速构建App界面的框架(●'◡'●) -----SalutJs

    前言 卤煮在公司之初接触到的是一个微信APP应用.前端技术采用的是Backbone+zepto等小型JS类库.在项目开发之初,这类中小型的项目采用这两种库可以满足基本的需求.然而,随着迭代的更新和业务 ...

  3. Linux3 在VMware中搭建CentOS6.5虚拟机

    前言:      本文主要是我在大家hadoop集群之前 ,需啊先安装CentOS虚拟机,记录在此,作为参考.如果能帮助到其他人,自然是更好啦. =========================== ...

  4. Lesson 19 Sold out

    Text 'The play may begin at any moment,' I said. 'It may have begun already,' Susan answered. I hurr ...

  5. .NET基础拾遗(6)ADO.NET与数据库开发基础

    Index : (1)类型语法.内存管理和垃圾回收基础 (2)面向对象的实现和异常的处理 (3)字符串.集合与流 (4)委托.事件.反射与特性 (5)多线程开发基础 (6)ADO.NET与数据库开发基 ...

  6. Java在DOS命令下的运行及其API文档制作过程

    该文档主要描述java程序在DOS命令下的运行,以及一些常用的命令 常用DOS命令: d: 回车 盘符切换 dir(directory):列出当前目录下的文件以及文件夹 md (make direct ...

  7. hibernate多对一单向关联

    关联是类(类的实例)之间的关系,表示有意义和值得关注的连接. 本系列将介绍Hibernate中主要的几种关联映射 Hibernate一对一主键单向关联Hibernate一对一主键双向关联Hiberna ...

  8. 常用js功能函数集合

    1.获取随机时间戳 function uniqueId(){         var a=Math.random,b=parseInt;         return Number(new Date( ...

  9. 从java文件和CS文件里查询方法使用次数工具

    前几天,领导让我找一下老系统(Java)里getRemoteUser方法都哪个文件用了,package是什么,方法被调用了多少次,当时因为着急,所以,直接人工找的,但是以后要是再出现,人工找就太讨厌了 ...

  10. 【Android】 context.getSystemService()浅析

    同事在进行code review的时候问到我context中的getSystemService方法在哪实现的,他看到了一个ClipBoardManager来进行剪切板存储数据的工具方法中用到了cont ...