iOS学习笔记-137.RunLoop05——Runloop相关类3_CFRunLoopTimerRef(NSTimer)为何定时有时会失败

RunLoop05——Runloop相关类3_CFRunLoopTimerRef(NSTimer)为何定时有时会失败

一、CFRunLoopTimerRef 主要说明

CFRunLoopTimerRef是基于时间的触发器

基本上说的就是NSTimer,它会受到runloop的mode的影响

GCD的定时器不受Runloop的mode的影响


二、NSTimer定时器不能用了问题演示

2.1 NSTimer定时器不能用了,图示

image

2.2 NSTimer定时器不能用了代码示例1

-(void)timer1{
    //该方法内部自动添加到runloop中,并且设置运行模式为默认
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
}

2.3 NSTimer定时器不能用了代码示例2

我们首先在主线程上创建一个定时器,并且把它添加到默认模式下。

-(void)timer2{
    //创建定时器
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    //2.添加定时器到runLoop中,指定runloop的运行模式为NSDefaultRunLoopMode
     /*
        第一个参数:定时器
        第二个参数:runloop的运行模式
       */
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}

三、NSTimer定时器不能用了解决方式

3.1 问题分析与解决方式说明

由前面的学习,我们知道,当我们的 UITextView 滚动的时候,我们主线程的运行模式是 UITrackingRunLoopMode。然而我们的定时器是添加在 NSDefaultRunLoopMode,模式下。它能再 UITrackingRunLoopMode下运行,那不就见鬼了吗?

说了这么多?那么我们的解决方式也就是来了,我们把定时器在添加到 UITrackingRunLoopMode 模式下,不就ok了吗?。除此之外,我们还可以把它添加到 NSRunLoopCommonModes模式下,因为 NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode


3.2 针对 2.2 中的解决方式1

-(void)timer1{
    //该方法内部自动添加到runloop中,并且设置运行模式为默认
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    //可以通过如下来修改它的运行模式
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
}

3.3 针对 2.2 中的解决方式2

推荐这种

-(void)timer1{
    //该方法内部自动添加到runloop中,并且设置运行模式为默认
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
    //可以通过如下来修改它的运行模式
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

3.4 针对 2.3 中的解决方式1

-(void)timer2{
    //创建定时器
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    //2.添加定时器到runLoop中,指定runloop的运行模式为NSDefaultRunLoopMode
     /*
        第一个参数:定时器
        第二个参数:runloop的运行模式
       */
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

    // 3.1 第一种方式,再把它添加到 UITrackingRunLoopMode
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
}

3.5 针对 2.3 中的解决方式2

推荐这种

-(void)timer2{
    //创建定时器
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    //NSRunLoopCommonModes = NSDefaultRunLoopMode + UITrackingRunLoopMode
    //占用,标签,凡是添加到NSRunLoopCommonModes中的事件 都会被同时添加到打上commmon标签的运行模式上
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

四、子线程中使用 NSTimer 定时器

4.1 子线程中不能运行 NSTimer 定时器?

我们创建了如下代码,

-(void)viewDidLoad{
    [NSThread detachNewThreadSelector:@selector(timer3) toTarget:self withObject:nil];
}

-(void)timer3{ 
    //创建一个定时器
    //该方法内部自动添加到runloop中,并且设置运行模式为默认
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];
}

我们发现,我们的定时器,不能工作了?那么怎么解决呢?

4.2 问题分析月解决方式说明

分析上面的问题,我们知道,NSTimer是需要 RunLoop 的。之前我们在主线程中运行创建NSTimer的时候,不需要自己手动来获取RunLoop那是因为,主线程中的 RunLoop 已经自动创建了。然而子线程中的 RunLoop 是需要我们手动创建的。而创建的方法就是调用 currentRunLoop 方法,如实我们就有了解决方案,就是自己创建 RunLoop 并且运行它。

4.3 解决示例代码

于是针对上面的代码,我们就有了下面的解决代码

-(void)timer3{
    //子线程中,默认是没有创建 RunLoop 的,可以通过 [NSRunLoop currentRunLoop] 来创建,它其实是一个懒加载
    NSRunLoop *currentRunLoop = [NSRunLoop currentRunLoop];

    //创建一个定时器
    //该方法内部自动添加到runloop中,并且设置运行模式为默认
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    //让 runloop 运行
    [currentRunLoop run];
}

除此之外,我们上面scheduledTimerWithTimeInterval,该方法内部自动添加到runloop中,并且设置运行模式为默认,它内部其实已经调用了[NSRunLoop currentRunLoop]。那么我们直接运行就行了。于是又有

-(void)timer3{
    //创建一个定时器
    //该方法内部自动添加到runloop中,并且设置运行模式为默认。那么我们可以知道,它内部其实已经调用了[NSRunLoop currentRunLoop]
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(run) userInfo:nil repeats:YES];

    [[NSRunLoop currentRunLoop] run];
}

五、GCD定时器——不受Runloop的mode的影响

NSTimer 会受到 RunLoop的mode的影响。然而GCD不会。

5.1 GCD定时器的使用。


-(void)gcdTimer{
    NSLog(@"%s",__func__);
   //1.创建GCD中的定时器
   /*
     第一个参数:source的类型DISPATCH_SOURCE_TYPE_TIMER 表示是定时器
     第二个参数:描述信息,线程ID
     第三个参数:更详细的描述信息
     第四个参数:队列,决定GCD定时器中的任务在哪个线程中执行
     */
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));

    //2.设置定时器(起始时间|间隔时间|精准度)
    /*
       第一个参数:定时器对象
       第二个参数:起始时间,DISPATCH_TIME_NOW 从现在开始计时
       第三个参数:间隔时间 1.0 GCD中时间单位为纳秒
       第四个参数:精准度 绝对精准0
       */
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);

    //3.设置定时器执行的任务
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"GCD---%@",[NSThread currentThread]);
    });

    //4.启动执行
    dispatch_resume(timer);
    self.timer = timer;
}

5.2 GCD定时器补充


DISPATCH_SOURCE_TYPE_TIMER         定时响应(定时器事件)
DISPATCH_SOURCE_TYPE_SIGNAL        接收到UNIX信号时响应

DISPATCH_SOURCE_TYPE_READ          IO操作,如对文件的操作、socket操作的读响应
DISPATCH_SOURCE_TYPE_WRITE         IO操作,如对文件的操作、socket操作的写响应
DISPATCH_SOURCE_TYPE_VNODE         文件状态监听,文件被删除、移动、重命名
DISPATCH_SOURCE_TYPE_PROC         进程监听,如进程的退出、创建一个或更多的子线程、进程收到UNIX信号

下面两个都属于Mach相关事件响应
    DISPATCH_SOURCE_TYPE_MACH_SEND
    DISPATCH_SOURCE_TYPE_MACH_RECV

下面两个都属于自定义的事件,并且也是有自己来触发
    DISPATCH_SOURCE_TYPE_DATA_ADD
    DISPATCH_SOURCE_TYPE_DATA_OR
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值