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閱讀 228,119評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,382評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,616評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,112評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,355評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,869評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,727評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,928評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,165評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,585評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,892評論 2 372

推薦閱讀更多精彩內容