今天我們來分析一下UITableViewCell的重用機制.
首先,我們要明白我們?yōu)槭裁葱枰褂眠@種機制,其次,這種機制的原理是什么.
我們先舉個例子來說明.一個UITableView中有許多需要顯示的cell,但是我們不可能每個都會瀏覽到,那么如果我們把這些數據全部都加載進去,是不是造成了內存的負擔呢.
我們所能顯示的區(qū)域通常只有一個屏幕的大小,那么那些屏幕之外的信息是不需要一次性全都加載完的,只有當我們滑動屏幕需要瀏覽的時候,我們才需要它加載進來.因此,就有了我們要介紹的這部分內容,UITabelViewCell的重用機制.
重用機制實現(xiàn)了數據和顯示的分離,并不為每個數據創(chuàng)建一個UITableViewCell,我們只創(chuàng)建屏幕可顯示的最大的cell個數+1,然后去循環(huán)重復使用這些cell,既節(jié)省空間,又達到我們需要顯示的效果.
這種機制下系統(tǒng)默認有一個可變數組NSMutableArray* visiableCells,用來保存當前顯示的cell.一個可變字典NSMutableDictnery* reusableTableCells,用來保存可重復利用的cell.(之所以用字典是因為可重用的cell有不止一種樣式,我們需要根據它的reuseIdentifier,也就是所謂的重用標示符來查找是否有可重用的該樣式的cell).
重用的寫法如下:
// 設置單元格 indexPath :單元格當前所在位置 -- 哪個分區(qū)哪一行等
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath //UITableViewDataSource
{
static NSString *identifier = @"cell" ;
//相當于從集合中找尋完全出屏幕的單元格.
// identifier : 因為一個表視圖中可能存在多種樣式的單元格,咱們把相同樣式的單元格放到同一個集合里面,為這個集合加標示符,當我們需要用到某種樣式的單元格的時候,根據不同的標示符,從不同的集合中找尋單元格.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier] ;
// 如果從集合中未找到單元格,也就是集合中還沒有單元格,也就是還沒有單元格出屏幕,那么我們就需要創(chuàng)建單元格
if (!cell)
{
// 創(chuàng)建cell的時候需要標示符(Identifier)是因為,當該cell出屏幕的時候需要根據標示符放到對應的集合中.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"] ;
return cell ;
}```
系統(tǒng)第一次執(zhí)行`- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath`這個方法的時候, reusableTableCells為空,`[tableView dequeueReusableCellWithIdentifier:identifier]`的返回值為nil,我們需要通過`[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier: identifier]`方式來創(chuàng)建.
當我們的數據過多,整個屏幕的cell顯示不完全時,這個方法的執(zhí)行情況是 :
(1) 先執(zhí)行`[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier: identifier]`創(chuàng)建整個屏幕能顯示的cell數+1的cell(當我們拖動UITableView的時候,第一個cell沒有移出屏幕,最下面的cell就已經存在),并指定相同或者不同的標示符identifier.把創(chuàng)建出的屏幕能顯示的cell全部都加入到visiableCells數組中(最后一個創(chuàng)建的先不加入數組),reusableTableCells為空.
(2)當我們拖動屏幕時,頂端的cell移出屏幕并加入到reusableTableCells字典中,鍵為identifier ,并把之前已經創(chuàng)建的但是沒有加入到visiableCells的cell加入到visiableCells數組中.
(3)當我們接著拖動的時候,因為reusableTableCells中已經有值,所以,當需要顯示新的cell,cellForRowAtIndexPath再次被調用,執(zhí)行`[tableView dequeueReusableCellWithIdentifier: identifier]`,返回一個標示符為identifier的cell。該cell移出reusableTableCells之后加入到visiableCells;頂端的cell移出visiableCells并加入到reusableTableCells.如果visiableCells數組中沒有找到identifier類型的cell,則再次重新alloc一個.
在iOS6之后系統(tǒng)加入了一種單元格注冊的方法.
`[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier: identifier] ;`
這個方法的作用是,當我們從重用隊列中取cell的時候,如果沒有,系統(tǒng)會幫我們創(chuàng)建我們給定類型的cell,如果有,則直接重用. 這種方式cell的樣式為系統(tǒng)默認樣式.
在設置cell的方法中只需要:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 重用隊列中取單元格 由于上面已經注冊過單元格,系統(tǒng)會幫我們做判斷,不用再次手動判斷單元格是否存在
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: identifier forIndexPath:indexPath] ;
return cell ;
}