美文网首页
iOS - 循环引用

iOS - 循环引用

作者: 码代码的小马 | 来源:发表于2021-05-16 22:51 被阅读0次

    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 - 循环引用

          本文链接:https://www.haomeiwen.com/subject/tehdjltx.html