NSTimer 引发的循环引用(内存泄漏)| NSTimer强引用

在iOS中使用NSTimer(定时器)不当会引发内存泄漏.

NSTimer 有多种创建方式,具体可以看这位朋友的文章:https://blog.51cto.com/u_16099225/6716123

我这里主要讲使用NSTimer 会引发的内存泄漏情况以及解决方法:

内存泄漏出现的场景:

VC A push 到VC B, VC B里启动了一个 NSTimer, 然后VC B push 到VC C (或者 pop 回VC A), 此时 VC B里的 NSTimer 仍在执行(这是不对的),造成的原因就是 VC B里的 NSTimer 没有被及时释放(销毁)掉,我们称之为内存泄漏.

常见的有以下创建方式:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor greenColor];

    __weak typeof(self) weakSelf = self;
    
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerClick) userInfo:nil repeats:YES];
}

- (void)timerClick
{
    NSLog(@"%s",__FUNCTION__);
}

- (void)dealloc
{
    NSLog(@"走了 dealloc方法");
    NSLog(@"%s",__FUNCTION__);
    [self.timer invalidate];
}

不管是 :self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerClick) userInfo:nil repeats:YES];

还是:NSTimer * timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerClick) userInfo:nil repeats:YES];

这两种创建的方式 都有一个 target: self, 意思是timer 强引用了当前的self (VC), 那么创建的时候当前的self (VC)又强引用了 timer,你引用我,我引用你,这就造成了循环引用,所以VC B push (或者pop)的时候, arc内存管理会判断我们还需要这个内存(PS:arc内存管理的模式是,自动销毁我们不需要的内存). 就没有帮我们销毁.

所以我们发现 VC B 退出后没有走 - (void)dealloc 方法,timer也就没有被销毁.

解决方法:

一般解决self强引用的问题,会使用弱引用 weakSelf 来代替 self,但是这里不行,因为weakSelf 是用在block 中的.所以解决方法有两种:

方法一:

一是给NSTimer 和 VCB之间加一个“中间人”

原先是互相循环引用:

加个“中间人”

1.1.新建一个继承于NSObject 的类 MiddleObject ,在 MiddleObject 里用 weak 指针定义一个属性target,再构造一个初始化的类方法.

MiddleObject.h 文件:

​​​​​​​MiddleObject.m 文件:

1.2.在VC B 中倒入 #import "MiddleObject.h". 

将target:self 中的 self 替换成 中间类 MiddleObject 

截图如下: 

代码如下:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor greenColor];


    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:[MiddleObject callWithTarget:self] selector:@selector(timerClick) userInfo:nil repeats:YES];
}

这样字就完成了

NSTimer 引用 MiddleObject,

MiddleObject 弱引用VC B

VC B 引用 NSTimer , 

三种关系,解决了两两循环引用的问题

方法二:

方法一有点麻烦,一般我们直接更换一种创建方法: 不使用 有 target的 方法:

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor greenColor];

    __weak typeof(self) weakSelf = self;
    
    //不使用target
       self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
           [weakSelf  timerClick];
       }];
}

- (void)timerClick
{
    NSLog(@"%s",__FUNCTION__);
}

- (void)dealloc
{
    NSLog(@"走了 dealloc方法");
    NSLog(@"%s",__FUNCTION__);
    [self.timer invalidate];
}

self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {

           [weakSelf  timerClick];

       }];

这种方法,没有target,本身不会造成循环引用, 且在block里也是有了 弱引用weakSelf开启定时器,所以 VC B 销毁的时候,timer 也会走- (void)dealloc方法,销毁掉timer .[self.timer invalidate];

方法三:自己手动销毁timer

虽然说现在都是arc自动销毁,但是我们也可以手动销毁,直接一点. 没有这么多华丽胡哨的.

创建方法还是有target的那种:

self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerClick) userInfo:nil repeats:YES];

在VC B 的 - (void)viewWillDisappear:(BOOL)animated 方法里,直接手动销毁timer

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    
    NSLog(@"%s",__FUNCTION__);
    
    [self.timer invalidate];
}

  • 12
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值