内容简介:UITableView是我们用的最多的一个控件,所以对于UITableView重用机制的掌握也就成了我们必须了解的一个知识点,对于UITableView重用机制的剖析网上已经有相当多的文章了,这里我结合图片和代码再来阐述一遍.我们在编写代码的时候经常会写到这样一段话,根据一个指定的标识符来获取到一个可重用的cell,那么这个实际上就使用到了UITableView的重用机制,用一张图来解释一下.实线包含起来的部分是显示到屏幕上的部分,其中Cell3,cell4,cell5是完全显示到屏幕上的,而cell2和
UITableView是我们用的最多的一个控件,所以对于UITableView重用机制的掌握也就成了我们必须了解的一个知识点,对于UITableView重用机制的剖析网上已经有相当多的文章了,这里我结合图片和代码再来阐述一遍.
cell = [tableView dequeueReusableCellWithIdentifier:@"identifier"]; 复制代码
我们在编写代码的时候经常会写到这样一段话,根据一个指定的标识符来获取到一个可重用的cell,那么这个实际上就使用到了UITableView的重用机制,用一张图来解释一下.
实线包含起来的部分是显示到屏幕上的部分,其中Cell3,cell4,cell5是完全显示到屏幕上的,而cell2和cell6是只有一部分显示到当前屏幕.如果当前正是向上滑动的一个过程的话,那么cell1这个时候已经被加入到了重用池当中,因为它已经滚出到屏幕外了,如果继续向上滑动的话,那么这个时候Cell7就会从重用池中根据指定的重用标识符来取出一个可重用的cell.如果cell1到cell7全部用同一个重用标识符的话,那么cell7就可以复用cell1创建的内存,这样就达到了这样一个重用的目的.
接下来,通过自定义一个控件来更深入理解UITableView的重用机制
首先,创建了一个叫 ViewReusePool
的类用于实现重用机制.
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> // 实现重用机制的类 @interface ViewReusePool : NSObject // 从重用池中取出一个可重用的View -(UIView *)dequeueReusableView; // 向重用池中添加一个view -(void)addUsingView:(UIView *)view; // 重置方法,将当前使用中的视图移动到可重用队列当中 -(void)reset; @end #import "ViewReusePool.h" @interface ViewReusePool() // 等待使用的队列 @property(nonatomic,strong) NSMutableSet *waitUsedQueue; // 使用中的队列 @property(nonatomic,strong) NSMutableSet *usingQueue; @end @implementation ViewReusePool -(instancetype)init { self = [super init]; if (self) { _waitUsedQueue = [NSMutableSet set]; _usingQueue = [NSMutableSet set]; } return self; } -(UIView *)dequeueReusableView { UIView *view = [_waitUsedQueue anyObject]; if (!view) { return nil; } // 进行队列移动 [_waitUsedQueue removeObject:view]; [_usingQueue addObject:view]; return view; } -(void)addUsingView:(UIView *)view { if (!view) { return; } //添加视图到使用中的队列 [_usingQueue addObject:view]; } -(void)reset { UIView *view = nil; while ((view = [_usingQueue anyObject])) { // 从使用队列中移除 [_usingQueue removeObject:view]; // 加入等待队列中 [_waitUsedQueue addObject:view]; } } @end 复制代码
这部分的代码简单明了的描述了重用机制的实现原理.接下来自定义一个使用了 ViewReusePool
重用机制的 IndexedTableView
.实现了类似索引条的功能.
#import <UIKit/UIKit.h> @protocol IndexedTableViewDataSource <NSObject> // 获取一个tableview的字母索引条数据的方法 - (NSArray<NSString *> *)indexTitlesForIndexTableView:(UITableView *)tableView; @end @interface IndexedTableView : UITableView @property (nonatomic,weak) id<IndexedTableViewDataSource> indexDataSource; @end #import "IndexedTableView.h" #import "ViewReusePool.h" @interface IndexedTableView () { UIView *containerView; ViewReusePool *reusePool; } @end @implementation IndexedTableView -(void)reloadData{ [super reloadData]; if (!containerView) { containerView = [[UIView alloc]initWithFrame:CGRectZero]; containerView.backgroundColor = [UIColor whiteColor]; // 避免索引条随着tableView滚动 [self.superview insertSubview:containerView aboveSubview:self]; } if (!reusePool) { reusePool = [[ViewReusePool alloc]init]; } // 标记所有视图为可重用状态 [reusePool reset]; // reload字母索引条 [self reloadIndexBar]; } -(void) reloadIndexBar { // 获取字母索引条的显示内容 NSArray <NSString *> *arrayTitles = nil; if ([self.indexDataSource respondsToSelector:@selector(indexTitlesForIndexTableView:)]) { arrayTitles = [self.indexDataSource indexTitlesForIndexTableView:self]; } //判断字母索引条是否为空 if (!arrayTitles || arrayTitles.count <= 0) { containerView.hidden = YES; return; } 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[i]; //从重用池中取出一个button来 UIButton *button = (UIButton *)[reusePool dequeueReusableView]; // 如果没有可重用的button就创建一个 if (!button) { button = [[UIButton alloc]initWithFrame:CGRectZero]; button.backgroundColor = [UIColor whiteColor]; //注册button到重用池中 [reusePool addUsingView:button]; NSLog(@"新建了一个button"); }else{ NSLog(@"button 重用了"); } //添加button到父视图控件 [containerView addSubview:button]; [button setTitle:title forState:UIControlStateNormal]; [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; //设置button的坐标 [button setFrame:CGRectMake(0, i * buttonHeight, buttonWidth, buttonHeight)]; } containerView.hidden = NO; containerView.frame = CGRectMake(self.frame.origin.x + self.frame.size.width - buttonWidth, self.frame.origin.y, buttonWidth, self.frame.size.height); } @end 复制代码
最后在viewcontroller中添加IndexedTableView并实现自定义的重用功能.
#import "ViewController.h" #import "IndexedTableView.h" @interface ViewController ()<UITableViewDelegate,UITableViewDataSource,IndexedTableViewDataSource> { IndexedTableView *tableView;//带有索引条的tableView UIButton *button; NSMutableArray *dataSource; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; //创建一个tableview tableView = [[IndexedTableView alloc]initWithFrame:CGRectMake(0, 60, self.view.frame.size.width, self.view.frame.size.height - 60) style:UITableViewStylePlain]; tableView.delegate = self; tableView.dataSource = self; // 设置tableview的索引数据源 tableView.indexDataSource = self; [self.view addSubview:tableView]; //创建一个button button = [UIButton buttonWithType:UIButtonTypeSystem]; button.frame = CGRectMake(0, 20, self.view.frame.size.width, 40); button.backgroundColor = [UIColor redColor]; [button setTitle:@"reloadTable" forState:UIControlStateNormal]; [button addTarget:self action:@selector(doAction) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:button]; //数据源 dataSource = [NSMutableArray array]; for (int i = 0; i < 100; i++) { [dataSource addObject:@(i + 1)]; } } #pragma mark IndexedTableViewDataSource -(NSArray<NSString *> *)indexTitlesForIndexTableView:(UITableView *)tableView { //奇数次调用返回6个字母,偶数次调用返回11个 static BOOL change = NO; if (change) { change = NO; return @[@"A",@"B",@"C",@"D",@"E",@"F",@"G",@"H",@"I",@"J",@"K"]; }else{ change = YES; return @[@"A",@"B",@"C",@"D",@"E",@"F"]; } } #pragma mark UITableViewDataSource -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return dataSource.count; } -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *identifier = @"reuseId"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier]; // 如果重用池当中没有可重用的cell,那么创建一个cell if (!cell) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; } // 文案设置 cell.textLabel.text = [[dataSource objectAtIndex:indexPath.row]stringValue]; return cell; } -(void)doAction{ [tableView reloadData]; } @end 复制代码
运行一下可以看出是这样一个效果.
点击reloadData会从重用池中取出可复用的button,没有的话就创建,看看控制台的打印.
刚进去的时候,因为重用池中没有button所以会创建6个button,并添加到重用池中.如下图所示.
点击reloadTable按钮会复用之前的6个button,并创建5个新的button添加到重用池中.如下图所示
这个时候,重用池中已经有11个button了,如果当前屏幕的button数不超过这个数的话,就会一直复用重用池中的button不会再创建新的button,这样减少了内存的开销.
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Golang 之 sync.Pool 对象池对象重用机制总结
- Java 代码重用:功能与上下文重用
- 重用和单一职责可能是对立的
- 利用Socket重用绕过payload受限
- 通过代码重用攻击绕过现代XSS防御
- asp.net mvc的代码重用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。