- 首先我們需要搞明白為什么要使用重用機(jī)制,它的原理是什么.
無(wú)論是UITableView還是UICollectionView,都有許多需要顯示的cell (item), 但是屏幕的大小是有限的有巧,一次只能顯示那么幾個(gè)参淫,如果我們把所有的數(shù)據(jù)全部都加載進(jìn)去,暫時(shí)又看不到志衍,就會(huì)非常浪費(fèi)內(nèi)存. - 那么該如何避免這種不必要的內(nèi)存消耗呢寿弱?就是每次只顯示屏幕能放得下的cell的數(shù)據(jù)馁痴,在用戶滑動(dòng)屏幕的過(guò)程中错沽,再去加載新的數(shù)據(jù)簿晓,
- 于是就有了cell的重用機(jī)制
重用機(jī)制實(shí)現(xiàn)了數(shù)據(jù)和顯示的分離,并不會(huì)為每個(gè)要顯示的數(shù)據(jù)都創(chuàng)建一個(gè)Cell,一般情況下只創(chuàng)建屏幕可顯示的最大的cell個(gè)數(shù)+1,每當(dāng)有一個(gè)cell從屏幕消失,就將其放到緩存池中千埃,如果有新的cell出現(xiàn)憔儿,就去緩存池中取,如果緩存池中沒(méi)有放可,再創(chuàng)建谒臼。
這種機(jī)制下系統(tǒng)默認(rèn)有一個(gè)可變數(shù)組 NSMutableArray* visiableCells, 用來(lái)保存當(dāng)前顯示的cell. 還有一個(gè)可變字典 NSMutableDictnery* reusableTableCells, 用來(lái)保存可重復(fù)利用的cell. 之所以用字典是因?yàn)榭芍赜玫腸ell有不止一種樣式,我們需要根據(jù)它的reuseIdentifier(重用標(biāo)識(shí)符)來(lái)查找是否有可重用的該樣式的cell.
重用的寫(xiě)法如下:
//設(shè)置單元格(cell) indexPath :單元格當(dāng)前所在位置-哪一組的哪一行
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ //定義重用標(biāo)識(shí)
static NSString *identifier = @"cell" ;
//identifier: 因?yàn)橐粋€(gè)表視圖中可能存在多種樣式的單元格(cell),所以要相同樣式的單元格放到同一個(gè)集合里面,并且為這個(gè) 集合綁定標(biāo)識(shí)符,當(dāng)我們需要用到某種樣式的單元格的時(shí)候,就根據(jù)不同的標(biāo)識(shí)符,從不同的集合中找尋單元格.
//該方法會(huì)先去緩存池中尋找對(duì)應(yīng)的cell 如果緩存池中沒(méi)有朝刊,就看有沒(méi)有注冊(cè)對(duì)應(yīng)的cell,如果也沒(méi)有注冊(cè)蜈缤,就看storyboard中有沒(méi) 有綁定對(duì)應(yīng)標(biāo)識(shí)的cell 都沒(méi)有的話就創(chuàng)建
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier] ;
if (!cell) {
//創(chuàng)建cell的時(shí)候需要標(biāo)示符(Identifier)是因?yàn)?當(dāng)該cell離開(kāi)屏幕的時(shí)候需要根據(jù)標(biāo)示符放到對(duì)應(yīng)的集合中.
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];
return cell ;
}
- 系統(tǒng)第一次執(zhí)行 cellForRowAtIndexPath:(NSIndexPath *)indexPath的時(shí)候, reusableTableCells為空,
[tableView dequeueReusableCellWithIdentifier:identifier] 的返回值為nil, 我們需要通過(guò) initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier: identifier] 方式來(lái)創(chuàng)建.
當(dāng)我們的數(shù)據(jù)過(guò)多,整個(gè)屏幕的cell顯示不完全時(shí),這個(gè)方法的執(zhí)行情況是 :
(1) 先執(zhí)行initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier: identifier]創(chuàng)建整個(gè)屏幕能顯示的cell數(shù)+1的cell (當(dāng)我們拖動(dòng)屏幕的時(shí)候,第一個(gè)cell沒(méi)有移出屏幕,最下面的cell就已經(jīng)存在), 并指定相同或者不同的標(biāo)示符identifier.把創(chuàng)建出的屏幕能顯示的cell全部都加入到visiableCells數(shù)組中(最后一個(gè)創(chuàng)建的先不加入數(shù)組)拾氓,reusableTableCells為空.
(2)當(dāng)我們拖動(dòng)屏幕時(shí),頂端的cell移出屏幕并加入到reusableTableCells字典中,鍵為identifier ,并把之前已經(jīng)創(chuàng)建的但是沒(méi)有加入到visiableCells的cell加入到visiableCells數(shù)組中.
(3)當(dāng)我們接著拖動(dòng)的時(shí)候,因?yàn)閞eusableTableCells中已經(jīng)有值,當(dāng)需要顯示新的cell底哥,cellForRowAtIndexPath再次被調(diào)用執(zhí)行[tableView dequeueReusableCellWithIdentifier: identifier]咙鞍,返回一個(gè)標(biāo)示符為identifier的cell。該cell移出reusableTableCells之后加入到visiableCells趾徽;頂端的cell移出visiableCells并加入到reusableTableCells.如果visiableCells數(shù)組中沒(méi)有找到identifier類型的cell,則再次重新alloc一個(gè).
在iOS6之后系統(tǒng)加入了一種單元格注冊(cè)的方法.
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier: identifier] ;
這個(gè)方法的作用是,當(dāng)我們從重用隊(duì)列中取cell的時(shí)候,如果沒(méi)有,系統(tǒng)會(huì)幫我們創(chuàng)建我們給定類型的cell,如果有,則直接重用. 這種方式cell的樣式為系統(tǒng)默認(rèn)樣式.
在設(shè)置cell的方法中只需要:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// 重用隊(duì)列中取單元格 由于上面已經(jīng)注冊(cè)過(guò)單元格,系統(tǒng)會(huì)幫我們做判斷,不用再次手動(dòng)判斷單元格是否存在
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: identifier forIndexPath:indexPath] ;
return cell ;
}