ios-定时器解析

首先有两个方法我们去了解下就可以了,其实定时器有很多方法代码如下所示。这个方法就是重复的去调用work方法,然后我们不用去添加到运行循环中

_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(work) userInfo:nil repeats:YES];
官方文档解释如下

另一种如下所示,这个是需要我们去把定时器添加到运行循环中的,解释也如下所示

_timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(work) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];

fire函数的作用也就是实时的去触发定时器,去调用定时器的方法,不会影响到定时器的周期,这里不会影响是什么意思呢,就是比如说我在1s的时候调用了定时器需要去调用的方法,然后我们在1.5s的时候又去调用了下面的方法,我们再在2s的时候还是会再去调用定时器去调用的方法。不会受到影响

 [self.timer fire];
效果图,这里其实就是在33.92秒的时候去调用了work方法,然后我们在34.47秒的时候又去调用了fire方法然后调用了work方法,然后34.92秒的时候又去调用了work方法。


当我们在子线程去创建定时器,并把定时器添加到运行循环中的时候,需要去开启运行循环,因为子线程的运行循环是默认不开启的。

[[NSRunLoop currentRunLoop]run];
还有就是需要注意的是,如果我们在子线程上开启定时器,不应该在主线程进行设置失效,不然的话就会造成runloop资源的浪费。如下例子所示

//首先在子线程中开始执行开启定时器的方法 
[NSThread detachNewThreadSelector:@selector(threadTime) toTarget:self withObject:nil];

-(void)threadTime
{
    _timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(work) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];

    [[NSRunLoop currentRunLoop]run];
    
    NSLog(@"结束循环");
}
//我们设置button点击使定时器失效,下面这是在主线程中
- (IBAction)buttonClick:(id)sender {
    
    [self.timer invalidate];
    NSLog(@"------");
}
打印结果如下所示,也就是说没有去调用结束循环的NSLog语句,所以这就说明我们子线程的runloop没有结束,因为runloop是一个死循环一样的东西,如果我们将定时器失效的,照理说runloop没有这个定时源了,应该也会一起失效的,但是这里并没有失效,如果我们在子线程中去执行使定时器失效的方法就可以了。

当我们在子线程中使定时器无效的时候输出结果如下所示



需要注意的是Runloop在同一时刻只能在一个mode上运行,其他mode上的任务暂停处理,这里再简单的说下NSRunLoopCommonModes,这个模式就相当于是NSDefaultRunLoopMode和NSEventTrackingRunLoopMode的结合.

当我们把timer的属性变成weak的时候,我们会发现如果我们调用的是下面这段代码程序会崩溃,因为_timer为空

    _timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(work) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
但是如果我们调用的是下面这个,就可以,为什么,因为下面这个是直接自己就把定时器添加到运行循环中,说明运行循环对它有一个强引用的关系。在这里即便我们把_timer变成为nil,定时器方法还是可以调用,因为_timer只是一个指针,指向一块内存,调用下面这个方法简单的说运行循环也指向这块内存,所以其实并不会让定时器没有效果。

_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(work) userInfo:nil repeats:YES];


在ios10.0的时候提供了这么一个方法,这样就不会用定时器对self的强引用的,这样self就可以正常销毁了,我们只需要在dealloc方法中去使定时器无效就可以停止定时器了,但是如果你在block里面直接使用了self,也是还会出现强引用的

  _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        
        NSLog(@"-------");
    }];
当然还有另一种方法

 [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        
    }];

GCD的定时器,如果这个timer是个局部变量那么下面就没办法去触发事件,但是如果我们在触发事件中使用了我们创建的timer那就可以正常使用了,因为在block中会对它做一次强引用。

 //获取一个全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //创建一个定时器,并将定时器的任务交给全局队列去执行,下面四个参数是    
     //type     dispatch源可处理的事件
     //handle     可以理解为句柄、索引或id,假如要监听进程,需要传入进程的ID
     //mask     可以理解为描述,提供更详细的描述,让它知道具体要监听什么
     //queue     自定义源需要的一个队列,用来处理所有的响应句柄(block)
    self.timer1 = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

    //设置触发的间隔的时间,第一个参数表示的是定时器的触发的间隔时间,第二0表示是允许的误差时间
    dispatch_source_set_timer(self.timer1, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC,  0 * NSEC_PER_SEC);
    //定时器的触发事件
    dispatch_source_set_event_handler(self.timer1, ^{
        
        if(!repeats)
        {
          //取消定时器
          dispatch_cancel(self.timer1);
        }
        NSLog(@"%@",[NSThread currentThread]);

    });
    //开始定时器
    dispatch_resume(self.timer1);



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值