UItableViewCell的復用機制,原理解答

今天在看iphone開發秘籍的時候,遇到這個問題,就仔細的深入了一下,通過測試,獲取了一些自認為還不錯的結論,希望對大家在cell復用方面遇到的一些問題會有所幫助。
本篇文章只講原理,對于如果對cell做界面,不深入講述。鑒于我的表達能力有限,可能會有我自己清楚,但是卻說不清楚的地方,如有問題,留言給我。

UITableView在界面的編程用的甚多,iphone開發也三月有余了,每次用到cellForRowAtIndexPath的委托方法的時候,都是直接copy代碼,自己略加一些界面的修改,對于cell的標示符都是static NSString* identifier = @"cell";然后調用dequeueReusableCellWithIdentifier方法獲取cell,如果cell為空,再調用[[[UITableViewCellalloc]initWithStyle方法新創建一個,根本沒有考慮過更深一些的東西。為了講解清楚,現放上一段代碼,代碼copy自iphone開發秘籍,本人為了講解,略加修改。以下所有講解均依照此代碼進行,因此,如果您希望能夠透徹的了解cell的復用機制,建議實際運行以下,跟著講解,查看效果。代碼只有tableVIew的委托方法,因此您需要自己創建工程,把這些委托方法加進去。

上文一共四個委托方法,表明tableView一個section,有32行,高度為58.關于tableView的高度那個委托方法的返回的高度值,建議最好自己運行程序查看以下,最終達到一頁的界面顯示8個cell,第8個cell顯示一半也行,但是不能顯示9和7個cell。我這里之所以為58,由于(480 - 44)/ 58 為7.5 的樣子,44為navigationBar的高度,480為屏幕的高度。總之,需要達到的效果是一頁顯示7個多的cell。還有那個icon.png需要自己準備了,要把它顯示出來。是不是很麻煩呀?沒辦法,誰讓我們在獲得知識呢,知識總是需要點功夫的。

[cpp] view plain copy

- (NSInteger)numberOfSectionsInTableView:(UITableView *)aTableView   
{   
    return 1;   
}  
  
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section   
{  
    return 32;  
}  
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath  
{  
    return 58;  
}  
- (UITableViewCell *)tableView:(UITableView *)tView cellForRowAtIndexPath:(NSIndexPath *)indexPath  
{  
    UITableViewCellStyle style;  
    NSString *cellType;  
      
    switch (indexPath.row % 4)  
    {  
        case 0:  
            style = UITableViewCellStyleDefault;  
            cellType = @"Default Style";  
            //有標題沒有正文(沒有細節文字)。可選的圖片  
            break;  
        case 1:  
            style = UITableViewCellStyleSubtitle;  
            cellType = @"Subtitle Style";  
            //標題和正文方式,上下排布。可選的圖片  
            break;  
        case 2:  
            style = UITableViewCellStyleValue1;  
            cellType = @"Value1 Style";  
            //左邊文字左對齊,右邊文字右對齊。可選的圖片  
            break;  
        case 3:  
            style =  UITableViewCellStyleValue2;  
            cellType =  @"Value2 Style";  
            //左邊文字右對齊,藍色;右邊文字左對齊,黑色。沒有圖片  
            break;  
              
    }  
    static int i = 0;  
    UITableViewCell *cell = [tView dequeueReusableCellWithIdentifier:cellType];  
    if (!cell)   
    {  
        cell = [[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:cellType] autorelease];  
        ++i;  
        NSLog(@"cell created %d times", i);  
    }  
      
    if (indexPath.row > 3)   
        cell.imageView.image = [UIImage imageNamed:@"icon.png"];  
      
    cell.textLabel.text = cellType;  
    cell.detailTextLabel.text = @"Subtitle text";  
    return cell;  
}  

首先講解一下復用隊列:
復用隊列的元素增加:只有在cell被滑動出界面的時候,此cell才會被加入到復用隊列中。每次在創建cell的時候,程序會首先通過調用dequeueReusableCellWithIdentifier:cellType方法,到復用隊列中去尋找標示符為“cellType”的cell,如果找不到,返回nil,然后程序去通過調用[[[UITableViewCell alloc] initWithStyle:style reuseIdentifier:cellType] autorelease]來創建標示符為“cellType”的cell。
先運行一次程序,不要滑動cell,查看打印日志,會有打印"cell create 1 times",,,"cell create 8 times",一共有8個日志,表明cell創建了8次,這個日志打印是在cellForRowAtIndexPath中的創建cell的時候打印的。然后慢慢向下(向下的意思,實際上是手向上滑動,讓界面顯示下一個cell,向上與之相反)滑動cell,到顯示第九個cell的時候,會看到打印一條日志"cell create 9 times",然后繼續慢慢向下滑動,會有"cell create 10 times",直到滑動到第12個cell以后,cell被創建了12個之后,以后再怎么滑動,cell都不會被創建了。也就是說本tableView要完整的工作,一共創建了12個cell。
開始解釋原因了:
第一頁的界面一共需要展示8個cell,故而cell需要創建8次,每一個cell負責自己的數據顯示。

此8個創建以后,復用隊列依然為空(因為你此時還沒有滑動cell呢,復用隊列的元素不會增加)。

注解

  • 首先根據屏幕的高度和cell的高度計算出,在屏幕上可見的cell的個數。在內存中創建可見個數的cell的內存空間。根據以上計算得出的cell的個數為8
  • 必須明白一點,8個cell創建后,當你滾動tableView,顯示第9個cell后,此時的復用隊列依然是空的,因為,第一個cell還處在屏幕的可見范圍內,當第一個cell徹底從屏幕消失的時候,復用隊列中才添加了第一個從屏幕消失的cell

然后在向下滑動顯示出第9個cell的時候,還會調用cellForRowAtIndexPath方法,在此方法中,它首先到可復用隊列中去找,由于此時隊列為空,它創建了一個cell,打印日志,同時當第1個cell滑動出界面之外,第一個cell進入到復用隊列中,隊列中有一個元素,此元素的表示符為@"Default Style"。然后繼續向下滑動cell,開始顯示第10個cell,它同樣到復用隊列中去找,第10個cell的標示符為@"Subtitle Style",但是隊列中唯一的cell的標示符為@"Default Style",根據標示符尋找,沒有找到,故而再次創建一個新的cell,同時將滑動出界面的第2個cell進入復用隊列。此時復用隊列有兩個元素,標示符為@"Default Style",@"Subtitle Style"。同樣的道理,滑動到第11個cell的時候,第3個cell入隊,第12個cell的時候,第4個cell入隊。此時復用隊列的元素個數為四個,標示符分別為:@"Default Style",@"Subtitle Style",@"Value1 Style",@"Value2 Style".然后繼續滑動cell,當滑動到第13個cell的時候,它的標示符為@"Default Style",它到復用隊列中去找,可以找到。故而,這個cell就不會被創建了,同理,再次向下滑動,以下的所有cell都可以根據標示符找到對應的cell,不會有被創建的。在向下滑動的過程中,滑動出去的cell會被入隊,不過只入隊創建了的cell,也就是說最終隊列中會有12個cell。對于每次取對應標示符的元素,到底取的是哪一個?采用的什么策略?這個我不知道,我通過打印查看的是在向下滑動的過程中,一直順著隊列找,隊列是一個循環隊列。向上滑動的時候,逆著隊列向上找,由于是循環隊列,一直在轉圈。
如果你仔細看的話,你會發現第一個cell本來沒有圖片,為什么一劃下去再次滑動(如果滑動的距離遠,一次搞定,如果滑動的距離近,多上下滑動幾次)上來又有圖片了呢?這個就要思考一個:第一個cell在滑出界面又劃入界面的時候,是從復用隊列拿到的。復用隊列有12個元素,第1,5,9還第一個cell有相同的標示符,第1個沒有圖片,第5個和第9個有圖片。當用戶復用的是第一個cell的時候,它是沒有圖片,當用戶復用第5,9個cell的時候,它是有圖片的。因此,當你上下滑動,查看第一個cell的時候,你會看到它一會有圖片,一會沒有圖片。這個跟復用時候的隊列查找規則有關。

實用篇:
說了那么多,全是關于原理的。現在說點實用的。如果你想在所有的cell中添加一個按鈕,你是應該在if中添加,還是應該在if之外添加呢?毫無疑問,應該在if中,如果你是在if的外面添加的,那會導致,你在向下滑動cell的過程中,取出來的cell本來已經帶有button了,而你還在addSubview,按鈕越來越多。或者你可以采用在if外面添加,前提是每次先cell remove掉其所有的子視圖。這樣太消耗cpu,麻煩了。如果你想一行隔著一行有按鈕和沒有按鈕,你該怎么做呢?稍微思考一下,這個可是兩種風格的cell,故而在滑出界面進行重用的時候,它們應該屬于不同的標示符。于是你在創建cell的時候,應該去指定兩種標示符,創建兩種cell。當然,也許你聰明了,我是不是可以在if之外先remove掉cell的所有子視圖,然后根據row % 2 == 0或者!=0 來進行addSubView:button嗎?答案當然是肯定的,但是這樣還是同樣的問題,太消耗cpu了。平常我們給每一個cell添加了一個button,肯定要添加事件的。對于不同的button,響應不同的事件?那么我是不是通過在if語句中給button設置它的tag標記(例如button.tag = indexpath.row)來實現呢?哈哈,你應該足夠聰明了吧,當然不行。你可以這樣想,if語句是用來創建的,它只被執行了(例如上面的例子:12次),但是你可能有幾百行的cell,當然你的tag也就只有12個了,明顯不對應。像這樣的,應該怎么處理呢?答案是:放在if的外面。你在if外面設置了tag標記,當然,在某一個具體的時間點上,仍然只有12個標記,但是這12個標記是可變的,例如當前界面顯示第100-111號的cell,那么此時的button的tag就會是100-111了,仍然是12個按鈕,但是它們會根據用戶的滑動,進行不同的tag切換,相當于擁有了很多個按鈕。如果你沒有被我說的話給弄暈,腦袋又足夠清醒的話,

你應該可以得出以下的結論:
對于界面的定制,放在if中比較好,一個cell中只創建一次

對于數據的定制,放在if外面比較好,對于不同的cell,表示不同的內容,雖然只有12個cell,但是cell中存放的數據我可以任意的映射。

如果你得出了這個結論,那么如果在加上textField,label等等,你應該可以輕松搞定。不僅僅是表面上,更重要的是,你理解了原理,掌握了機制,萬變都不怕,即使有新的需求,腦袋想想,或者拿著這篇文章看看,希望能給你一些啟示。

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

推薦閱讀更多精彩內容