前言
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 ininitWithFrame:reuseIdentifier:
and cannot be changed thereafter.
AUITableView
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 thedequeueReusableCellWithIdentifier:
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

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 theregisterNib:forCellReuseIdentifier:
orregisterClass: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