1. What is circular reference
A circular reference occurs when two objects, A and B, strongly reference each other. When A is released, B must be released, and WHEN B is released, A must be released. So that no one can be released
From the point of reference counting: when referencing each other, the double method reference technologies are +1, resulting in no case that the reference technologies can be zero, never free, and cannot free their memory, even if no variable holds them
2. A simple example
Declare A TableViewController from A VC push to A TableViewController, print it in the VC and cell dealloc to see if the TableViewController and cell are released, and pop it to see if the TableViewController and cell are released
// // TableViewController.m // MemoryManageDemo // // Created by Ternence on 2021/5/16. // #import "TableViewController.h" #import "TableViewCell.h" @interface TableViewController ()<UITableViewDelegate, UITableViewDataSource> @property (nonatomic, strong) UITableView *tableView; @end @implementation TableViewController - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor lightTextColor]; [self setupUI]; } - (void)setupUI { self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; self.tableView.delegate = self; self.tableView.dataSource = self; [self.tableView registerClass:[TableViewCell class] forCellReuseIdentifier:NSStringFromClass(TableViewCell.class)]; [self.view addSubview:self.tableView]; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 1; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 50; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:NSStringFromClass(TableViewCell.class)]; if (cell == nil) { cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NSStringFromClass(TableViewCell.class)]; } return cell; } - (void)dealloc { NSLog(@" %@ dealloc", self); } @endCopy the code
@implementation TableViewCell
- (void)dealloc {
NSLog(@"cell dealloc %@", self);
}
@end
Copy the code
The output is as follows
The 2021-05-16 22:17:00. 030361 + 0800 MemoryManageDemo [80460-4120492] < TableViewController: 0x7FC53E205E10 > Dealloc 2021-05-16 22:17:00.032229+0800 MemoryManageDemo[8046:4120492] cell Dealloc <TableViewCell: 0x7fc53cc24ee0; baseClass = UITableViewCell; frame = (0 0; 390 50); autoresize = W; layer = <CALayer: 0x600003a59fe0>>Copy the code
Under normal circumstances, both the VC and the Cell are released
We modify the cell code. Declare the attribute tableView in the cell, and use the cell to strongly reference the tableView (tableView is the default strong reference cell), check whether the cell can be released
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:NSStringFromClass(TableViewCell.class)];
if (cell == nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NSStringFromClass(TableViewCell.class)];
}
cell.tableView = tableView;
return cell;
}
Copy the code
@interface TableViewCell : UITableViewCell
@property (nonatomic, strong) UITableView *tableView;
@end
Copy the code
Print:
2021-05-16 22:20:35.357511+0800 MemoryManageDemo[81366:4128143] <TableViewController: 0x7FDB6260e8e0 > deallocCopy the code
At this point, the VC release, but the cell dealloc is not executed, that is, the cell is not released, this is because at this time the tableView forcibly references the cell, the cell in turn forcibly references the tableView, forming a circular reference, so even if the VC release, tableView and cell still cannot be released
Solution: Set the cell’s modifier weak, that is, the cell weakly references the tableView
@interface TableViewCell : UITableViewCell
@property (nonatomic, weak) UITableView *tableView;
@end
Copy the code
Output print:
The 2021-05-16 22:24:34. 911475 + 0800 MemoryManageDemo [81779-4133416] < TableViewController: 0x7FD05CE10DF0 > dealloc 2021-05-16 22:24:34.913045+0800 MemoryManageDemo[81779:4133416] cell Dealloc <TableViewCell: 0x7fd05ce12ab0; baseClass = UITableViewCell; frame = (0 0; 390 50); autoresize = W; layer = <CALayer: 0x600002ac2720>>Copy the code
Now the cell is free, and the tableView must be free
It can be seen from the above that the constituent condition of circular reference is mutual strong reference, and the solution is to break the mutual strong reference, where one party weakly references the other party
Vc -> tableView -> cell -> vc vc -> tableView -> cell -> vc vc
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewCell *cell = (TableViewCell *)[tableView dequeueReusableCellWithIdentifier:NSStringFromClass(TableViewCell.class)];
if (cell == nil) {
cell = [[TableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NSStringFromClass(TableViewCell.class)];
}
cell.vc = self;
return cell;
}
Copy the code
@interface TableViewCell : UITableViewCell
@property (nonatomic, strong) UIViewController *vc;
@end
Copy the code
When I print, I find nothing, that is, the VC is not released, the cell is not released, and this is also strong reference, because; In order for the VC to be released, it has to be released the cell, and in order for the cell to be released, it has to be released the TableView, and in order for the TableView to be released, it has to be released the VC, which forms a circular reference ring