UITableView is one of the most used controls, so we must understand the UITableView reuse mechanism to master a knowledge point, for the analysis of UITableView reuse mechanism online has quite a lot of articles, here I combine the picture and code to elaborate again.
cell = [tableView dequeueReusableCellWithIdentifier:@"identifier"];
Copy the code

When we write code, we often write a paragraph like this, according to a specified identifier to obtain a reusable cell, then this actually uses the UITableView reuse mechanism, with a diagram to illustrate.

Next, take a closer look at the reuse mechanism of UITableView by customizing a control

First, a class called ViewReusePool was created to implement the reuse mechanism.

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>@interface ViewReusePool: NSObject retrieves a reusable View from the reuse pool -(UIView *)dequeueReusableView; // add a view to the pool -(void)addUsingView:(UIView *)view; // Resets the current view to a reusable queue -(void)reset; @end#import "ViewReusePool.h"@interface ViewReusePool() // Queue to be used @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) {returnnil; } // Queue move [_waitUsedQueue removeObject:view]; [_usingQueue addObject:view];return view;
}

-(void)addUsingView:(UIView *)view
{
    if(! view) {return; } // Add a view to the queue in use [_usingQueue addObject:view]; } -(void)reset { UIView *view = nil;while((view = [_usingQueue anyObject])) {// Remove [_usingQueue removeObject:view]; // Join the queue [_waitUsedQueue addObject:view]; } } @endCopy the code

This section of the code is a straightforward description of how the reuse mechanism works. The next step is to define an IndexedTableView using the ViewReusePool reuse mechanism. Realizes the function similar to index bar.

#import <UIKit/UIKit.h>@ protocol IndexedTableViewDataSource < NSObject > / / get a tableview letters index method of the data - (NSArray < nsstrings * > *)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]; Article / / avoid index as the tableView rolling [self. The superview insertSubview: containerView aboveSubview: self]. }if(! reusePool) { reusePool = [[ViewReusePool alloc]init]; } // mark all views as reusable [reusePool reset]; // reload index bar [self reloadIndexBar]; } -(void) reloadIndexBar {NSArray <NSString *> *arrayTitles = nil;if([self.indexDataSource respondsToSelector:@selector(indexTitlesForIndexTableView:)]) { arrayTitles = [self.indexDataSource indexTitlesForIndexTableView:self]; } // Determine if the alphabetic index bar is emptyif(! 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]; UIButton *button = (UIButton *)[reusePool dequeueReusableView]; UIButton *button = (UIButton *)[reusePool dequeueReusableView]; // If there are no reusable buttons, create oneif(! button) { button = [[UIButton alloc]initWithFrame:CGRectZero]; button.backgroundColor = [UIColor whiteColor]; // register button to reusePool [reusePool addUsingView:button]; NSLog(@"Created a new button.");
        }else{
            NSLog(@"Button reused"); } // Add a button to the parent view control [containerView addSubview:button]; [buttonsetTitle:title forState:UIControlStateNormal];
        [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; // Set the coordinates of the buttonsetFrame: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
Copy the code

Finally, add IndexedTableView to viewController and implement custom reuse.

#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]; / / create a tableview tableview = [[IndexedTableView alloc] initWithFrame: CGRectMake (0, 60, the self. The frame. The size, width, self.view.frame.size.height - 60) style:UITableViewStylePlain]; tableView.delegate = self; tableView.dataSource = self; / / set the tableview index data source tableview. IndexDataSource = self; [self.view addSubview:tableView]; / / create a button button = [UIButton buttonWithType: UIButtonTypeSystem]; button.frame = CGRectMake(0, 20, self.view.frame.size.width, 40); button.backgroundColor = [UIColor redColor]; [buttonsetTitle:@"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 < > nsstrings * *) indexTitlesForIndexTableView tableView: (UITableView *) {/ / odd times call returns six letters, even time call returns 11 static BOOL change = NO;if (change) {
        change = NO;
        return@ [@"A"The @"B"The @"C"The @"D"The @"E"The @"F"The @"G"The @"H"The @"I"The @"J"The @"K"];
    }else{
        change = YES;
        return@ [@"A"The @"B"The @"C"The @"D"The @"E"The @"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]; // If there are no reusable cells in the pool, create a cellif(! cell) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; } / / copywriter set cell. TextLabel. Text = [[dataSource objectAtIndex: indexPath. Row] stringValue];return cell;
}
-(void)doAction{
    [tableView reloadData];
}
@end
Copy the code

If I run it, I can see that it looks like this.

At first, because there are no buttons in the reuse pool, six buttons are created and added to the reuse pool. As shown in the figure below.

Clicking the reloadTable button will reuse the previous 6 buttons and create 5 new buttons to add to the reuse pool. As shown in the figure below

At this point, there are already 11 buttons in the reuse pool. If the number of buttons on the current screen does not exceed this number, the pool will be reused and no new buttons will be created, thus reducing the memory overhead.