iOS - 循環引用

1. 什麼是循環引用

當兩個對象A和B, 分別強引用對方,那麼就會產生循環引用。即A釋放的時候必須先釋放B,而B釋放的時候必須釋放A。致使誰也不能釋放markdown

從引用計數的角度解釋: 互相引用的時候,雙法引用技術都是+1的,致使任何狀況下引用技術都不能爲0,始終沒法釋放,沒法釋放他們的內存,即便沒有變量持有他們atom

2. 一個簡單的例子

聲明一個TableViewController, 從A vc push 到TableViewController, 在vc和cell的 dealloc中打印看是否釋放,pop的時候查看TableViewControllercell是否釋放spa

//
//  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);
}

@end
複製代碼
@implementation TableViewCell

- (void)dealloc {
    NSLog(@"cell dealloc %@", self);
}

@end
複製代碼

輸出以下code

2021-05-16 22:17:00.030361+0800 MemoryManageDemo[80460:4120492]  <TableViewController: 0x7fc53e205e10> dealloc
2021-05-16 22:17:00.032229+0800 MemoryManageDemo[80460:4120492] cell dealloc <TableViewCell: 0x7fc53cc24ee0; baseClass = UITableViewCell; frame = (0 0; 390 50); autoresize = W; layer = <CALayer: 0x600003a59fe0>>
複製代碼

可知,正常狀況下,VC和Cell都釋放了orm

咱們修改cell的代碼。cell中聲明屬性tableView, 並用cell強引用tableView,(tableView是默認強引用cell的), 查看此時cell是否能釋放對象

- (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;
}
複製代碼
@interface TableViewCell : UITableViewCell

@property (nonatomic, strong) UITableView *tableView;

@end
複製代碼

打印:內存

2021-05-16 22:20:35.357511+0800 MemoryManageDemo[81366:4128143]  <TableViewController: 0x7fdb6260e8e0> dealloc
複製代碼

此時,vc釋放了,但celldealloc沒執行,即cell沒釋放,這是由於此時tableView強引用cellcell又強引用tableView,構成了循環引用,因此此時即便vc釋放了,tableViewcell仍是沒法釋放it

解決方案: 將cell的修飾符用weak修飾,即cell弱引用tableViewio

@interface TableViewCell : UITableViewCell
@property (nonatomic, weak) UITableView *tableView;
@end
複製代碼

輸出打印:table

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>>
複製代碼

此時cell正常釋放,tableView確定也是釋放的

由上可知,循環引用的構成條件是相互強引用,解決方案也是打破相互強引用,其中一方弱引用另外一方便可

咱們把代碼再改一下,由cell再也不引用tableView,而是引用vc,即: vc -> tableView -> cell -> 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;
}
複製代碼
@interface TableViewCell : UITableViewCell
@property (nonatomic, strong) UIViewController *vc;
@end
複製代碼

打印發現什麼都沒有,即vc沒釋放,cell也沒釋放,此時也夠成了強引用, 這是由於;vc要釋放,必須先釋放cell,而cell要釋放,必須先釋放tableview,tableview要釋放,必須先釋放vc,這樣構成了循環引用環

相關文章
相關標籤/搜索