UITableviewCell复用机制

前言

UITableview在iOS中的使用频率是非常高的.通常,我们只需要通过设置代理,并且在代理方法tableView:cellForRowAtIndexPath: 调用dequeueReusableCellWithIdentifier:获取cell并直接使用.但是从没有细致得想过其中的过程与机制,并且知道最近面试(此次面试的问题)的时候,才发现自己表述得不太好.这种感觉就好像你每天都准时吃饭(滑稽),突然有一天有人问你人为什么吃饭的时候自己却没能及时答上来的那种感觉.说明自己学得不够扎实.as we know,

如果你不能将知识通过简洁的语言表达出来,那说明你还没掌握这个知识.

借此机会,将这个知识点记录下来

reuseIdentifier

对于reuseIdentifier,官方文档是这样解释的:

The reuse identifier is associated with a UITableViewCell object that the table-view’s delegate creates with the intent to reuse it as the basis (for performance reasons) for multiple rows of a table view. It is assigned to the cell object in initWithFrame:reuseIdentifier: and cannot be changed thereafter.
A UITableView object maintains a queue (or list) of the currently reusable cells, each with its own reuse identifier, and makes them available to the delegate in the dequeueReusableCellWithIdentifier: method.

冒死翻译一下:
这个复用标识关联UITableviewCell对象,在tableview代理中创建带有”标识符”来复用cell对象,而且作为tableview多行显示的原型(性能的原因).通过initWithFrame:reuseIdentifier:来指定一个cell对象而且在调用这个方法之后就不能修改了.在UITableView对象维护一个当前复用的cell队列(或列表),并且每一个cell都拥有自己的标识符,并这些cell能在代理对象的dequeueReusableCellWithIdentifier:的方法中获取.

tableview新建的时候,会新建一个复用池(reuse pool).这个复用池可能是一个队列,或者是一个链表,保存着当前的Cell.pool中的对象的复用标识符就是reuseIdentifier,标识着不同的种类的cell.所以调用dequeueReusableCellWithIdentifier:方法获取cell.从pool中取出来的cell都是tableview展示的原型.无论之前有什么状态,全部都要设置一遍.

过程

UITableView创建同时,会创建一个空的复用池.之后UITableView在内部维护这个复用池.一般情况下,有两种用法,一种是在取出一个空的cell的时候再新建一个.一种是预先注册cell.之后再直接从复用池取出来用,不需要初始化.

  • UITableview第一次执行tableView:cellForRowAtIndexPath:.当前复用池为空.
    dequeueReusableCellWithIdentifier调用中取出cell,并检测cell是否存在.
    目前并不存在任何cell,于是新建一个cell,执行初始化, 并return cell.
    代码如下:

    #define kInputCellReuseIdentifierPWD   @"password_cell"
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kInputCellReuseIdentifierPWD];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kInputCellReuseIdentifierPWD];
        // other initialization i.e. add an UIButton to cell's contentView
    }
    // custom cell. i.e. change cell's title
    cell.textLabel.text = @"It is awesome";
    return cell;
    

上面的代码中,你返回的cell会被UITableView添加到复用池中.第二次调用tableView:cellForRowAtIndexPath:,当前复用池中有一个cell.这时候因为UITableView上面还未填满,而且复用池中唯一的那一个已经在使用了.
所以取出来的Cell仍然是nil.于是继续新建一个cell并返回,复用池再添加一个cell,当前复用池中cell的个数为2.
假如当前tableview只能容纳5个cell.那么在滚动到第6个cell时,从tableview的复用池取出来的cell将会是第0行的那个cell.以此类推,当滚动到第7行时,会从复用池取出来第1行的那个cell. 另外,此时不再继续往复用池添加新的cell.

  • 另一个用法:在UITableView初始化的时候,注册一个UITableViewCell的类.

    [tableView registerClass:[UITableViewCell class]
             forCellWithReuseIdentifier:@"AssetCell"];
    

使用此方法之后,就不用再判断取出来的cell是否为空,因为取出来的cell必定存在.调用dequeueReusableCellWithIdentifier:方法时,会先判断当前复用池时候有可用复用cell.
如果没有,tableview会在内部帮我们新建一个cell,其他的跟方法一一样.
这里有个动画可以很清晰地看到滑动时的复用过程.图片来自Scroll Views Inside Scroll Views

复用cell的过程
复用cell的过程

StoryBoard中的复用机制

CSwater讨论之后,觉得在StoryBoard中,复用机制如下:在对StoryBoard初始化UITableView时,会再内部调用registerClass:forCellWithReuseIdentifier:注册我们在storyboard上设置的cell.剩下的流程跟第二种方法一样.

爬过的坑

从复用池中获取cell的方法有两个:dequeueReusableCellWithIdentifier:forIndexPath:
dequeueReusableCellWithIdentifier:.还记得我们在第二个方法对使用使用复用池之前的情况吗? 对,就是注册一个cell.注册之后我们调用dequeueReusableCellWithIdentifier:获取cell.
对于第二种情况,两种方法都是没问题的.但是,在调用registerClass:forCellReuseIdentifier:之前,你必须注册一个cell类.正如官方文档所言:

IMPORTANT
You must register a class or nib file using the registerNib:forCellReuseIdentifier: or registerClass:forCellReuseIdentifier: method before calling this method.

总结

UITableViewCell的复用机制是,在tableview中存在一个复用池.这个复用池是一个队列或一个链表.然后通过dequeueReusableCellWithIdentifier:获取一个cell,如果当前cell不存在,即新建一个cell,并将当前cell添加进复用池中.如果当前的cell数量已经到过tableview所能容纳的个数,则会在滚动到下一个cell时,自动取出之前的cell并设置内容.

扩展知识

其实,UITableViewCell的复用机制并非什么黑魔法,而是设计模式中的一种创造型设计模式--对象池设计模式

The object pool pattern is a software creational design pattern that uses a set of initialized objects kept ready to use – a "pool" – rather than allocating and destroying them on demand. A client of the pool will request an object from the pool and perform operations on the returned object. When the client has finished, it returns the object to the pool rather than destroying it; this can be done manually or automatically. ---- wikipedia

大意是,对象池模式是一种创造型设计模式,使用一个已初始化对象的集合,并能随时从”池”中拿出来使用.以此避免对象的创建销毁所带来的开销.一个客户端的池会请求池中的对象,然后在返回的对象上执行操作.当这个客户端结束时,代替原本销毁操作,取而代之的是将对象返回到池中.这个操作可以手动执行,也可以自动执行.有一个明显的有点就是面对数据量很大时,能很好的改善性能.更多的请看维基百科.

参考文章

Object pool pattern
Scroll Views Inside Scroll Views
UITableViewDataSource Protocol Reference
UITableView

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容