Block的循环引用解决方案理解

本文介绍了Block循环引用的问题及其原因,并详细讲解了两种解决方法:使用__weak修饰对象和通过Block参数传递对象。解释了每种方法的原理、优缺点,建议优先使用__weak修饰以避免循环引用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Block的循环引用原理和解决方法大家都比较熟悉,此处将结合上文的介绍,介绍一种不常用的解决Block循环引用的方法和一种借助Block参数解决该问题的方法。
Block循环引用原因:一个对象A有Block类型的属性,从而持有这个Block,如果Block的代码块中使用到这个对象A,或者仅仅是用用到A对象的属性,会使Block也持有A对象,导致两者互相持有,不能在作用域结束后正常释放。
解决原理:对象A照常持有Block,但Block不能强引用持有对象A以打破循环。
解决方法
方法一: 对Block内要使用的对象A使用_*_weak*进行修饰,Block对对象A弱引用打破循环。

有三种常用形式:

  • 使用__weak ClassName
    __block XXViewController* weakSelf = self;
    self.blk = ^{
        NSLog(@"In Block : %@",weakSelf);
    };
  • 使用__weak typeof(self)
    __weak typeof(self) weakSelf = self;
    self.blk = ^{
        NSLog(@"In Block : %@",weakSelf);
    };
  • Reactive Cocoa中的@weakify和@strongify
    @weakify(self);
    self.blk = ^{
        @strongify(self);
        NSLog(@"In Block : %@",self);
    };

其原理参考《@weakify, @strongify》,自己简便实现参考《@weak - @strong 宏的实现》

方法二:对Block内要使用的对象A使用__block进行修饰,并在代码块内,使用完__block变量后将其设为nil,并且该block必须至少执行一次

    __block XXController *blkSelf = self;
    self.blk = ^{
        NSLog(@"In Block : %@",blkSelf);
    };

注意上述代码仍存在内存泄露,因为:

  • XXController对象持有Block对象blk
  • blk对象持有__block变量blkSelf
  • __block变量blkSelf持有XXController对象
    __block XXController *blkSelf = self;
    self.blk = ^{
        NSLog(@"In Block : %@",blkSelf);
        blkSelf = nil;//不能省略
    };
    
    self.blk();//该block必须执行一次,否则还是内存泄露

在block代码块内,使用完使用完__block变量后将其设为nil,并且该block必须至少执行一次后,不存在内存泄露,因为此时:

  • XXController对象持有Block对象blk
  • blk对象持有__block变量blkSelf(类型为编译器创建的结构体)
  • __block变量blkSelf在执行blk()之后被设置为nil(__block变量结构体的__forwarding指针指向了nil),不再持有XXController对象,打破循环

第二种使用__block打破循环的方法,优点是:

  • 可通过__block变量动态控制持有XXController对象的时间,运行时决定是否将nil或其他变量赋值给__block变量
  • 不能使用__weak的系统中,使用__unsafe_unretained来替代__weak打破循环可能有野指针问题,使用__block则可避免该问题

缺点也明显:

  • 必须手动保证__block变量最后设置为nil
  • block必须执行一次,否则__block不为nil循环应用仍存在

因此,还是避免使用第二种不常用方式,直接使用__weak打破Block循环引用。
方法三:将在Block内要使用到的对象(一般为self对象),以Block参数的形式传入,Block就不会捕获该对象,而将其作为参数使用,其生命周期系统的栈自动管理,不造成内存泄露。
即原来使用__weak的写法:

    __weak typeof(self) weakSelf = self;
    self.blk = ^{
        __strong typeof(self) strongSelf = weakSelf;
        NSLog(@"Use Property:%@", strongSelf.name);
        //……
    };
    self.blk();

改为Block传参写法后:

    self.blk = ^(UIViewController *vc) {
        NSLog(@"Use Property:%@", vc.name);
    };
    self.blk(self);

优点:

  • 简化了两行代码,更优雅
  • 更明确的API设计:告诉API使用者,该方法的Block直接使用传进来的参数对象,不会造成循环引用,不用调用者再使用weak避免循环

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值