UI視圖-UITableView重用池機(jī)制及優(yōu)化

UITableView重用機(jī)制和原理
cell = [tableView dequeueReusableCellWithIdentifier:identifier];

滑動(dòng)過(guò)程中,A3,A4,A5全部顯示在屏幕上,A1移除屏幕時(shí),就被放到重用池中,當(dāng)A7即將顯示在屏幕上時(shí),從重用池中根據(jù)指定的≈取出可重用的cell,
如果A1-A7為同一種identifier標(biāo)識(shí)符的話,A7就可以復(fù)用A1所創(chuàng)建的cell的內(nèi)存或者控件,就達(dá)到了cell復(fù)用的機(jī)制

實(shí)現(xiàn)一個(gè)tableView的索引表的重用池 (Demo百度網(wǎng)盤(pán))

默認(rèn)一加載是6個(gè)索引,當(dāng)點(diǎn)擊頁(yè)面上一個(gè)按鈕時(shí),變成10個(gè)索引,在此點(diǎn)擊變回6個(gè)索引,依次循環(huán)


1.按上圖自定義tableview,添加變量containerView和reusePool,其中containerView添加在tableView的最上方, reusePool繼承NSObject,用它來(lái)實(shí)現(xiàn)復(fù)用池
2.復(fù)用池用兩個(gè)NSMutableSet集合來(lái)實(shí)現(xiàn),一個(gè)為usingQueue,一個(gè)為waitUsedQueue

3.在tableView的reloadData方法里面(這個(gè)方法在數(shù)據(jù)源一開(kāi)始會(huì)調(diào)用一次,后面手動(dòng)調(diào)用),懶加載初始化reusePool,首先按照?qǐng)D一重置兩個(gè)隊(duì)列
  然后通過(guò)代理,從VC中獲取索引數(shù)組(6或者10)
  循環(huán)生成數(shù)組個(gè)數(shù)的button添加到contentview上顯示,這一步用的是復(fù)用池
4.復(fù)用池是,每次循環(huán),都按照?qǐng)D二,首先從等待隊(duì)列中取button,如果取到了,將它移動(dòng)到使用隊(duì)列中并使用,若沒(méi)取到,創(chuàng)建一個(gè)button并將它添加到使用隊(duì)列中使用

5.每次刷新,重走3和4,則實(shí)現(xiàn)了一直復(fù)用的效果

備注:圖三即為,初始是索引為6,全部為新建的,點(diǎn)擊按鈕reloadData,索引為11
則有6個(gè)是復(fù)用的,剩下的為新建的,此時(shí)隊(duì)列中有11個(gè)button
再點(diǎn)擊按鈕reloadData,索引為6,全部復(fù)用
.....

下面兩圖為每次reloadDate時(shí)復(fù)用池的工作過(guò)程




#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
// 實(shí)現(xiàn)重用機(jī)制的類
@interface ViewReusePool : NSObject

// 從重用池當(dāng)中取出一個(gè)可重用的view
- (UIView *)dequeueReusableView;

// 向重用池當(dāng)中添加一個(gè)視圖
- (void)addUsingView:(UIView *)view;

// 重置方法,將當(dāng)前使用中的視圖移動(dòng)到可重用隊(duì)列當(dāng)中
- (void)reset;

@end

#import "ViewReusePool.h"

@interface ViewReusePool ()
// 等待使用的隊(duì)列
@property (nonatomic, strong) NSMutableSet *waitUsedQueue;
// 使用中的隊(duì)列
@property (nonatomic, strong) NSMutableSet *usingQueue;
@end

@implementation ViewReusePool

- (id)init{
    self = [super init];
    if (self) {
        _waitUsedQueue = [NSMutableSet set];
        _usingQueue = [NSMutableSet set];
    }
    return self;
}

// 從重用池當(dāng)中取出一個(gè)可重用的view
- (UIView *)dequeueReusableView{
    UIView *view = [_waitUsedQueue anyObject];
    if (view == nil) {
        return nil;
    }
    else{
        // 進(jìn)行隊(duì)列移動(dòng)
        [_waitUsedQueue removeObject:view];
        [_usingQueue addObject:view];
        return view;
    }
}

// 向重用池當(dāng)中添加一個(gè)視圖
- (void)addUsingView:(UIView *)view
{
    if (view == nil) {
        return;
    }
    
    // 添加視圖到使用中的隊(duì)列
    [_usingQueue addObject:view];
}

// 重置方法,將當(dāng)前使用中的視圖移動(dòng)到可重用隊(duì)列當(dāng)中
- (void)reset{
    UIView *view = nil;
    while ((view = [_usingQueue anyObject])) {
        // 從使用中隊(duì)列移除
        [_usingQueue removeObject:view];
        // 加入等待使用的隊(duì)列
        [_waitUsedQueue addObject:view];
    }
}

@end

每次reloadData時(shí)

  // 標(biāo)記所有視圖為可重用狀態(tài)
    [reusePool reset];
//
  NSUInteger count = arrayTitles.count;
    CGFloat buttonWidth = 60;
    CGFloat buttonHeight = self.frame.size.height / count;
    
    for (int i = 0; i < [arrayTitles count]; i++) {
        NSString *title = [arrayTitles objectAtIndex:i];
        
        // 從重用池當(dāng)中取一個(gè)Button出來(lái)
        UIButton *button = (UIButton *)[reusePool dequeueReusableView];
        // 如果沒(méi)有可重用的Button重新創(chuàng)建一個(gè)
        if (button == nil) {
            button = [[UIButton alloc] initWithFrame:CGRectZero];
            button.backgroundColor = [UIColor whiteColor];
            
            // 注冊(cè)button到重用池當(dāng)中
            [reusePool addUsingView:button];
            NSLog(@"新創(chuàng)建一個(gè)Button");
        }
        else{
            NSLog(@"Button 重用了");
        }
        
        // 添加button到父視圖控件
        [containerView addSubview:button];
        [button setTitle:title forState:UIControlStateNormal];
        [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
        
        // 設(shè)置button的坐標(biāo)
        [button setFrame:CGRectMake(0, i * buttonHeight, buttonWidth, buttonHeight)];
    }
reloadData

reloadData是異步繪制的
[tableView reloadData]并不會(huì)等待tableview更新結(jié)束后才執(zhí)行后續(xù)代碼
而是立即執(zhí)行后續(xù)代碼,然后異步地去計(jì)算tableView的高度,獲取cell等等
如果表中的數(shù)據(jù)非常大,在一個(gè)run loop周期沒(méi)執(zhí)行完,
這時(shí)就顯示tableView視圖數(shù)據(jù)的操作就會(huì)出問(wèn)題了。
解決方法是:

1. 通過(guò)layoutIfNeeded方法,強(qiáng)制重繪并等待完成。

[self.tableView reloadData];  
[self.tableView layoutIfNeeded];  
//刷新完成,執(zhí)行后續(xù)需要執(zhí)行的代碼
 
2.reloadData方法會(huì)在主線程執(zhí)行,通過(guò)GCD,使后續(xù)操作排隊(duì)在reloadData后面執(zhí)行。

[self.tableView reloadData];  
dispatch_async(dispatch_get_main_queue(), ^{  
    //刷新完成,執(zhí)行后續(xù)代碼
});
數(shù)據(jù)源同步

我們?cè)谒⑿聰?shù)據(jù)時(shí)會(huì)面臨下面的問(wèn)題,某些用戶交互的操作是在主線程進(jìn)行的,而網(wǎng)絡(luò)和數(shù)據(jù)解析是在子線程進(jìn)行的,當(dāng)主線程做了例如刪除行A的操作并刷新了UI后,若之后子線程數(shù)據(jù)返回,因?yàn)樽泳€程拿到的是刪除前的數(shù)據(jù)拷貝,當(dāng)它處理完數(shù)據(jù)后,返回到主線程刷新UI,因?yàn)榇藭r(shí)子線程是有行A的,但主線程是沒(méi)有行A的,刷新后主線程就會(huì)又出現(xiàn)行A,此時(shí)就會(huì)出現(xiàn)數(shù)據(jù)問(wèn)題

方案一:并發(fā)訪問(wèn),數(shù)據(jù)拷貝,會(huì)增加內(nèi)存開(kāi)銷

在主線程刪除時(shí)記錄下來(lái),之后在子線程返回?cái)?shù)據(jù)將要更新UI時(shí),同步一下記錄的刪除操作,也就是在子線程中也刪除,再回到主線程就是同步的


方案二:串行訪問(wèn),若子線程的數(shù)據(jù)解析很慢,那么主線程的刪除操作就會(huì)有延遲
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,658評(píng)論 1 32
  • 好文章,轉(zhuǎn)載一下,有機(jī)會(huì)好好研究下 今天在研究SDWebImage和ASIHTTPRequest實(shí)現(xiàn)網(wǎng)絡(luò)圖片異步加...
    Apollo2016閱讀 2,119評(píng)論 0 2
  • 一、初始化方法 1:TableView風(fēng)格設(shè)置。 - (instancetype)initWithFrame:(C...
    iOS_SXH閱讀 1,976評(píng)論 1 10
  • UITableView在iOS開(kāi)發(fā)中占據(jù)非常重要的位置,必須熟練掌握。學(xué)習(xí)UITableView之前,先了解一下一...
    SmithJackyson閱讀 8,991評(píng)論 1 11
  • 今天做了一個(gè)變態(tài)的運(yùn)動(dòng),累并快樂(lè)。 生命在于運(yùn)動(dòng)果然不是騙我的。 只是今天的進(jìn)度不理想,還有很多很多東西要做~人生...
    林納啊閱讀 92評(píng)論 0 0

友情鏈接更多精彩內(nèi)容