UITableView 複用機制

複用機制

UITableView 首先加載可以覆蓋一屏幕的 UITableViewCell(具體個數要根據每一個 cell 的高度而定)。ui

而後當咱們往上滑動時(往下滑動同理),須要一個新的 cell 放置在列表的下方。此時,咱們不去生成新的 cell 而是先從 UITableView 的複用池裏去獲取,該池存放了已經生成的可以複用的 cell ,若是該池爲空,纔會主動建立一個新的 cell 。spa

複用池的 cell 是這樣被添加至該池的:當咱們向上滑動視圖時(向下滑動同理),位於最頂部的 cell 會相應地往上運動,直至消失在屏幕上。當其消失在視圖中時,會被添加至當前 UITableView 的複用池。code

所以,在渲染海量數據的列表時,並不須要不少 cell ,這得益於 UITableView複用機制orm

存在問題

在使用 UITableView 時,咱們可使用 dequeueReusableCellWithIdentifier: 方法實現 cell 實例的複用。內存

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 定義 cell 標識。
    static NSString *CellIdentifier = @"Cell";

    // 從複用池獲取 cell 實例(可能爲空)。
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];


    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    // 對 cell 進行簡單地數據配置.
    // 偶數行的 title 文字顏色設置爲紅色。
    if (indexPath.row % 2 == 0) {
        [cell.titleLabel setTextColor:[UIColor redColor]];
        cell.titleLabel.text = @"Title of Even Row";
    } else {
        cell.detailLabel.text = @"Detail";
    }

   return cell;
}
複製代碼

在上述代碼中,咱們但願偶數行的標題顏色設置爲紅色、標題內容爲 "Title of Even Row"、詳情內容爲空,但願奇數行的標題爲空、詳情內容爲 "Detail" 。開發

可是,當咱們滑動列表時,發現樣式錯亂了:偶數行的詳情內容不爲空、奇數行的標題不爲空。rem

該問題存在的根源在於 cell 實例的複用機制:當咱們沒有顯式地設置 cell 的樣式和內容時,它會繼續沿用回收前的樣式和內容設置string

解決辦法

面對樣式錯亂的問題,咱們有這樣一種解決方法:在從複用池得到一個 cell 實例時,咱們須要顯示地設置它的全部樣式和內容。該方法確實可以解決問題,但對於開發人員來講,心智壓力是巨大的。it

咱們須要「心智壓力不那麼大」的解決方法。io

方案一

方案描述:取消 cell 的複用機制,每次渲染都選擇建立新的 cell 實例,將原有的 dequeueReusableCellWithIdentifier: 方法替換爲 cellForRowAtIndexPath:

缺點:沒法複用 cell ,在列表項較多時存在內存佔用過大的問題。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 定義 cell 標識。
    static NSString *CellIdentifier = @"cell";

    // 經過 indexPath 建立 cell 實例,使得對於不一樣的 indexPath ,
    // 其對應的 cell 實例是不一樣的,從而解決問題。
    UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];

    if (!cell) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    // 對 cell 進行簡單地數據配置.
    // 偶數行的 title 文字顏色設置爲紅色。
    if (indexPath.row % 2 == 0) {
        [cell.titleLabel setTextColor:[UIColor redColor]];
        cell.titleLabel.text = @"Title of Even Row";
    } else {
        cell.detailLabel.text = @"Detail";
    }

    return cell;
}
複製代碼

方案二

方案描述:爲每一個 cell 根據 indexPath 建立惟一的標識符

缺點:雖然可複用 cell ,但在列表項較多時仍存在內存佔用過大的問題。

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 根據 indexPath 定義 cell 標識。
    NSString *CellIdentifier = [NSString stringWithFormat:@"cell%ld%ld",indexPath.section,indexPath.row];

    // 從複用池獲取 cell 實例(可能爲空)。
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (!cell) {
        // 當複用池獲取的 cell 實例爲空時,須要建立新的 cell 實例。
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }

    // 對 cell 進行簡單地數據配置.
    // 偶數行的 title 文字顏色設置爲紅色。
    if (indexPath.row % 2 == 0) {
        [cell.titleLabel setTextColor:[UIColor redColor]];
        cell.titleLabel.text = @"Title of Even Row";
    } else {
        cell.detailLabel.text = @"Detail";
    }

    return cell;
}
複製代碼

方案三

方案描述:每從複用池得到一個 cell 實例時,當其不爲空時,咱們須要刪除其全部的子視圖

該方案可實現 cell 實例的複用,成功解決了問題,應該是最好的解決方案

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // 定義 cell 標識。
    static NSString *CellIdentifier = @"Cell";

    // 從複用池獲取 cell 實例(可能爲空)。
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    if (!cell) {
        // 當複用池獲取的 cell 實例爲空時,須要建立新的 cell 實例。
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    } else {
        // 當複用池獲取的 cell 實例不爲空時,刪除其全部子視圖,使其變「乾淨」。
        while ([cell.contentView.subviews lastObject] != nil) {
            [(UIView *)[cell.contentView.subviews lastObject] removeFromSuperview];
        }
    }

    // 對 cell 進行簡單地數據配置.
    // 偶數行的 title 文字顏色設置爲紅色。
    if (indexPath.row % 2 == 0) {
        [cell.titleLabel setTextColor:[UIColor redColor]];
        cell.titleLabel.text = @"Title of Even Row";
    } else {
        cell.detailLabel.text = @"Detail";
    }

    return cell;
}
複製代碼
相關文章
相關標籤/搜索