NSTimer深入理解

NSTimer,即计时器,用于定时执行一些任务,一次或者多次。

系统Foundation框架提供的最常用方法如下,创建一个NSTimer,并将它放到当前runloop的default mode中。

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti // 执行任务的时间间隔 
                      target:(id)aTarget // 执行任务的对象
                     selector:(SEL)aSelector // 执行任务的对象方法
                     userInfo:(nullable id)userInfo // 用于传一些参数
                     repeats:(BOOL)yesOrNo; // 是否重复执行

1、怎么保证在未来某个时间点,要执行任务时,target还有效呢?target完全有可能被释放了呀。鉴于此,NSTimer会持有target对象,直到NSTimer invalidate。不同的是,一次性NSTimer会在执行完任务之后,会自动invalidate;而重复性NSTimer,则需要手动invalidate,否则会造成内存泄漏。   

2、由于NSTimer会持有target对象,如果刚好target对象就是self,而NSTimer又是self的一个实例变量,就会引发循环引用:self->NSTimer->self。

@interface TimerTest () {
    NSTimer *_timer; // self持有_timer
}

@end

@implementation TimerTest

- (id)init
{
    if (self = [super init]) {
        _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(test) userInfo:nil repeats:YES]; // _timer持有self
    }
    return self;
}

- (void)dealloc
{
    // 因为_timer持有self,所以self的引用计数>=1,也就执行不到dealloc。
    [_timer invalidate]; // 而执行不到dealloc,这句也就执行不到,_timer会一直持有self。这时候只能其他地方将_timer invalidate才行。
}

- (void)test
{

}

@end

为了解决这种循环引用问题,主要有两种方法:

1)实现一个NSTimer的category,并提供block接口。

@implementation NSTimer (Safe)

+ (NSTimer *)safeScheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                        repeats:(BOOL)repeats
                                            blk:(void(^)())blk {
    return [self scheduledTimerWithTimeInterval:interval
                                         target:self // target变成了NSTimer类对象,类对象由系统自动回收
                                       selector:@selector(_doBlock:) // NSTimer会把自己传过去
                                       userInfo:[blk copy] // 把block拷贝到堆上,避免栈block到用时已经被回收了
                                        repeats:repeats];
}

+ (void)_doBlock:(NSTimer *)timer {
    void(^blk)() = timer.userInfo;
    if(blk) {
        blk();
    }
}

@end

@interface TimerTest () {
    NSTimer *_timer; // self持有_timer
}
 
@end
 
@implementation TimerTest
 
- (id)init
{
    if (self = [super init]) {
__weak TimerTest *weakSelf = self; _timer
= [NSTimer safeScheduledTimerWithTimeInterval:1 repeats:YES blk:^{
__strong TimerTest *strongSelf = weakSelf;
[strongSelf test];
}]; } return self; } - (void)dealloc { [_timer invalidate]; } - (void)test { } @end 

3、NSTimer必须放在runloop中才能生效。如下方法就只是创建一个NSTimer,而并没有把它放到runloop中。

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti 
                            target:(id)aTarget 
                          selector:(SEL)aSelector 
                          userInfo:(nullable id)userInfo 
                           repeats:(BOOL)yesOrNo;

需要调用如下方法把它放到runloop中,才能生效。 

- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode;

 

https://developer.apple.com/documentation/foundation/nstimer/2091889-scheduledtimerwithtimeinterval

转载于:https://www.cnblogs.com/yangwenhuan/p/9010856.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值