iOS - 循环引用

简介: iOS - 循环引用

1. 什么是循环引用


当两个对象A和B, 分别强引用对方,那么就会产生循环引用。即A释放的时候必须先释放B,而B释放的时候必须释放A。导致谁也不能释放


从引用技术的角度解释:


互相引用的时候,双法引用技术都是+1的,导致任何情况下引用技术都不能为0,始终无法释放,无法释放他们的内存,即使没有变量持有他们


2. 一个简单的例子


声明一个TableViewController, 从A vc push 到TableViewController, 在vc和cell的 dealloc中打印看是否释放,pop的时候查看TableViewControllercell是否释放

//
//  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


输出如下

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都释放了


我们修改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还是无法释放


解决方案:


将cell的修饰符用weak修饰,即cell弱引用tableView

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


输出打印:

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,这样构成了循环引用环


相关文章
|
iOS开发
iOS循环引用深入理解
循环引用也就是引用之后造成的一个循环用图理解吧
235 0
iOS循环引用深入理解
|
编解码 API iOS开发
iOS - NSTimer循环引用
iOS - NSTimer循环引用
|
C++ iOS开发
iOS - Block 循环引用
我们知道,循环引用即: 当两个对象A和B, 分别强引用对方,那么就会产生循环引用。即A释放的时候必须先释放B,而B释放的时候必须释放A。导致谁也不能释放 而打破循环引用的方法就是其中一方弱引用另一方
|
编解码 iOS开发
|
22天前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
82 1
|
28天前
|
设计模式 安全 Swift
探索iOS开发:打造你的第一个天气应用
【9月更文挑战第36天】在这篇文章中,我们将一起踏上iOS开发的旅程,从零开始构建一个简单的天气应用。文章将通过通俗易懂的语言,引导你理解iOS开发的基本概念,掌握Swift语言的核心语法,并逐步实现一个具有实际功能的天气应用。我们将遵循“学中做,做中学”的原则,让理论知识和实践操作紧密结合,确保学习过程既高效又有趣。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你打开一扇通往iOS开发世界的大门。
|
29天前
|
搜索推荐 IDE API
打造个性化天气应用:iOS开发之旅
【9月更文挑战第35天】在这篇文章中,我们将一起踏上iOS开发的旅程,通过创建一个个性化的天气应用来探索Swift编程语言的魅力和iOS平台的强大功能。无论你是编程新手还是希望扩展你的技能集,这个项目都将为你提供实战经验,帮助你理解从构思到实现一个应用的全过程。让我们开始吧,构建你自己的天气应用,探索更多可能!
52 1