解决NSTimer的循环引用问题

使用过NSTimer的应该都清楚,NSTimer会和调用对象之间循环引用,从而导致内存泄漏。下面我们通过一个小测试,来说明这个问题。我们在一个VC的viewDidLoad方法里开启一个timer,在VC的dealloc方法里停止这个timer,如果没有循环引用,那么当我们退出这个VC之后,会调用VC的dealloc方法,从而停止timer,相关代码如下:

@interface TimerVC ()

@property (nonatomic, strong) NSTimer *timer;

@end

@implementation TimerVC

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

- (void)startTimer {
    if (!_timer) {
        _timer = [NSTimer timerWithTimeInterval:2 target:self selector:@selector(pollingTimer:) userInfo:nil repeats:YES];
        [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
    }
    [_timer fire];
}

- (void)stopTimer {
    [self.timer invalidate];
    self.timer = nil;
}

- (void)pollingTimer:(NSTimer *)timer {
    NSLog(@"*****************TimerVC pollingTimer");
}

- (void)dealloc {
    [self stopTimer];
}

@end

运行之后发现,即便退出了TimerVC,其内部的timer仍然没有停止,一直在后台打印log,而且不会调用TimerVC的dealloc方法,这是因为timer和VC相互引用,导致谁也不会被释放。为了解决这个问题,iOS 10中新增了两个API,允许我们使用block的方式执行定时代码,这两个API如下:

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;

我们把上面的startTimer方法改成如下方式:

- (void)startTimer {
    if (!_timer) {
        _timer = [NSTimer timerWithTimeInterval:2 repeats:YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"*****************TimerVC pollingTimer");
        }];
        [[NSRunLoop mainRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
    }
    [_timer fire];
}

重新执行,发现关闭TimerVC之后,stopTimer方法得以执行,说明使用block方式确实能解决循环引用。但使用block的方式也会有两个至关重要的问题:
1. 我们必须在调用者的dealloc方法里手动停止timer,否则timer会一直执行,并不被释放。
2. 请找一个只支持ios 10以上的公司。。。

我们的项目中使用的是另外一种方式,代码在这里:github。当我们在项目中比如一个VC里用上述代码初始化Timer时,各对象的引用关系如下:

这里写图片描述

这样,当退出VC时,LifeTracker由于没有持有者而被回收,从而走到了LiferTracker的dealloc方法里,我们在dealloc方法里调用了timer的invalidate方法,并将timer置为nil,这时,timerProxy也没有了持有者,所以也会被释放。整个过程没有内存泄漏,而且调用者不用手动调用timer的invalidate,方便快捷。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值