相亲app开发,解决内存循环引用的问题

循环引用是什么

ARC已经出来很久了,自动释放内存的确很方便,但是在相亲app开发应用中,并非绝对安全绝对不会产生内存泄露。导致iOS对象无法按预期释放的一个无形杀手是——循环引用。循环引用可以简单理解为A引用了B,而B又引用了A,双方都同时保持对方的一个引用,导致任何时候引用计数都不为0,始终无法释放。若当前对象是一个ViewController,则在dismiss或者pop之后其dealloc无法被调用,在频繁的push或者present之后内存暴增,然后APP就挂了。

iOS内存中的分区有:堆、栈、静态区。其中,栈和静态区是操作系统自己管理回收,不会造成循环引用。在堆中的相互引用无法回收,有可能造成循环引用。

循环引用的实质:多个对象相互之间有强引用,不能施放让系统回收。

解决循环引用一般是将 strong 引用改为 weak 引用。

循环引用场景

基础控制器代码:

#import "ViewController.h"

/// 前两种解决循环引用
typedef void(^CPBlock)(void);
/// 用于第三种解决循环引用
typedef void(^CPParamBlock)(ViewController *);

@interface ViewController ()
/// 前两种解决循环引用示例
@property(nonatomic, copy) CPBlock block;
/// 用于第三种解决循环引用示例
@property(nonatomic, copy) CPParamBlock paramBlock;
@property(nonatomic, copy) NSString *name;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.name = @"CPBlock";
}
@end

Block循环引用示例代码

Block 是我们常用到的,也是我们最要注意循环引用的。在此,先拿一个我们使用 Block 的例子:

//1. self持有block
    self.block = ^(void){
        // block持有self
        NSLog(@"%@", self);
        // block持有self的属性name
        NSLog(@"%@", self.name);
    };
  1. 当self自身要释放的时候, 就需要先释放block,
  2. 要释放block, 就要先把执行体内部block对self的持有先释放,
  3. self的retainCount != 0, self.block的retainCount != 0, 两者永远不会被释放, 造成循环引用

Delegate

delegate 属性的声明如下:

@property (nonatomic, weak) id <TestDelegate> delegate;

如果将 weak 改为 strong,则会造成循环引用

// self -> AViewController
BViewController *bVc = [BViewController new];
bVc = self; 
[self.navigationController pushViewController: bVc animated:YES];

   // 假如是 strong 的情况
   // bVc.delegate ===> AViewController (也就是 A 的引用计数 + 1)
   // AViewController 本身又是引用了 <BViewControllerDelegate> ===> delegate 引用计数 + 1
   // 导致: AViewController <======> Delegate ,也就循环引用啦

解决循环应用

方法一. 弱引用方式解决相亲app开发的循环应用代码 __weak

-(void)resolveRetainCycle_1{
    __weak typeof(self) weakSelf = self;
    self.block = ^(void){
        NSLog(@"%@", weakSelf);
        // 如果block执行体内有耗时操作, 或者异步操作, 那么我们还需要优化循环引用的解决方案
        // strongSelf为block执行体内局部变量, 在block执行体范围内使用, 当block被释放, strongSelf也会被自动释放
        __strong typeof(weakSelf)strongSelf = weakSelf;
        // 以此异步延迟2秒任务模拟耗时操作
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", strongSelf.name);
        });
    };
    self.block();
}

方法二. 截获变量方式 __block

-(void)resolveRetainCycle_2{
    // 截获当前ViewController
    // 需要手动释放 vc = nil;
    __block ViewController *vc = self;
    self.block = ^(void){
        // 以此异步延迟2秒任务模拟耗时操作
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", vc.name);
            vc = nil;
        });
    };
    self.block();
}

方法三. 通讯传值方式

/// 解决循环引用方法三
-(void)resolveRetainCycle_3{
    // 把self(实际为当前控制器: ViewController)作为一个临时变量传进block执行体
    // 不会造成循环引用
    self.paramBlock = ^(ViewController *vc){
        // 以此异步延迟2秒任务模拟耗时操作
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"%@", vc.name);
        });
    };
    self.paramBlock(self);
}

这样,相亲app开发就能解决循环引用的问题了。

声明:本文由云豹科技转发自麻蕊老师博客,如有侵权请联系作者删除

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值