其實有點失望,因為用google搜索“uitableviewcell dequeueReusableCellWithIdentifier”出來一堆堆資深博主的文章。看了看,大部分都是在解決一個問題:使用重用時cell顯示混亂的問題。該問題本身並不讓我失望,失望的是博主們的解釋。
首先,回顧一下UITableViewCell的重用,其基本邏輯就是tableView一開始會創建一屏幕的cell(如果有那么多)並把他們標記(Identifier),之后用戶上下滑動tableView時,使用Identifier獲取移出去的cell,將其用作新移進來的cell重新被賦予內容,再顯示出來,這樣就完成了cell重用,目的是優化內存。現在,我並不對其原理做再深入的探究,只是知道以上這些大概的邏輯。(歡迎探討,以及指出你認為我可能理解錯的地方)
問題來了,我在工作中、或是在前文提到的資深博主文章里會經常會看到有人說重用發生問題,新移入的cell有時會顯示出剛剛移除去的cell的內容,或者干脆就在滑動的過程中cell顯示完全混亂了。於是他們有寫人就創造了一些方法和解釋。比如:
http://www.cnblogs.com/lihaibo-Leao/p/3471556.html
http://blog.csdn.net/hmt20130412/article/details/20860049
http://blog.csdn.net/joiningss/article/details/6702023
http://blog.csdn.net/winsdom123456/article/details/7363383
各種解決方案歸納一下無外乎:
1,重用遇到界面混亂,這是個常見問題!我們就要使用多種的方法來干掉這個重用機制。(某博主原話,無力吐槽。。。)
2,既然重用有問題,那么我們就不重用了,有多少數據,就創建多少cell。
3,重用歸重用,但是identifier用不同的(好自欺欺人啊= =#)
4,重用歸重用,但我每次設置cell上的subView內容時,先刪除上面的所有subView,再重新創建。
遇到問題解決問題、分享解決方法這是好的,但是這里他們忽略了幾個問題:
1,iOS為什么一定要重用?重用優化內存,這是apple的全職工程師研究出來的東西,你能輕易否定它嗎?
2,identifier是用來標記可被重用的cell的隊列的,如果你使用不同的標記來標記cell,創建的時候也使用不同的標記創建,這是自欺欺人的,實際上也沒用重用;
3,每次刪除,再創建subView,看起來比較保險,也算是幾個解決方案中最靠譜的方法了。。。但其實,這對效率是一種損失。重用機制本來就是希望優化內存,優化界面體驗,你刪除再創建,增加不必要的工作量,就與這一理念背道而馳。
吐槽完畢,現在我利用一個demo(在這里可以看到https://github.com/pigpigdaddy/TableViewTestDemo)解釋一下我理解的正確的cell重用
先介紹一下demo:我創建了一個空項目,又創建了三個類,
類ViewController,用於window的rootViewController,
類TableViewTestView,在這里面創建tableview,
類DemoTableViewCell,繼承自UITableViewCell,里面有一個label,加在了cell的contentView上。
在ViewController創建TableViewTestView的實例,再在TableViewTestView里創建tableView,實現各種tableView的回調、數據源。用的cell是DemoTableViewCell (具體還請一定去https://github.com/pigpigdaddy/TableViewTestDemo看源代碼,很簡單,我就不對每個函數做做完全的說明了)
那么正確的重用應該是:
1,首先,用戶滑動tableview,在每次刷新新的cell時會自動調用
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *),你應該老老實實地去實現它
1 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 2 { 3 return self.dataSource.count; 4 } 5 6 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 7 { 8 static NSString *cellIdentifier = @"DemoTableViewCell"; 9 DemoTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; 10 if (!cell) { 11 cell = [[DemoTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; 12 } 13 14 [cell loadData:[self.dataSource objectAtIndex:indexPath.row]]; 15 16 return cell; 17 }
2,每次調用該函數時,就像我們所知道的那樣,先去根據identifier去尋找“隊列里”是否有可以重用的cell,如果沒有,則創建一個新的,所用的identifier是同一個!
3,刷新數據,你只需要調用我自定義cell里的函數
- (void)loadData:(NSDictionary *)data;
1 [cell loadData:[self.dataSource objectAtIndex:indexPath.row]];
該函數負責修改label的text,你不需要先刪除,在重新創建label,完全不需要。
4,那么label應該在哪里創建呢?在cell初始化的地方創建:
1 - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier 2 { 3 self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; 4 if (self) { 5 // Initialization code 6 self.labelTest = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 24)]; 7 [self.contentView addSubview:self.labelTest]; 8 } 9 return self; 10 }
像這樣,僅僅在這個cell被創建的時候,創建labelText,以后刷新到這個cell,只需要調用loadData這個函數,用相應的數據去刷新label內容就可以了!只要你正確刷新數據,顯示就不會有問題!
運行程序,上下滑動,完全沒有問題!
自定義的cell有再多的subView,這些subView只在cell創建的時候創建,其余時候刷新cell,只需要loadData刷新數據,刷新subView顯示內容就可以了,就是這么簡單!

再也不用擔心會混亂,再也不用找各種方法去規避“重用機制”了。希望大家曾經發生混亂的都去試試!
寫的不對的地方請多包涵,並幫忙指出,感謝!
