定时器 与 NSRunLoop

GCD定时器


Snip20170409_3.png

注意点:下面代码中的timer是一个局部的变量,走出代码块,被销毁;所以定时器中的方法不会执行

/*
     * @param dispatchQueue
     * @param intervalInSeconds 间隔时间秒
     * @param leewayInSeconds 允许的误差 如果传0 为没有误差
     *
     */
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"++++++++++++++++++++%@", [NSThread currentThread]);
    });
    dispatch_resume(timer);

对上面的解决办法:内部有一个强引用 引用着timer

@interface TestViewController ()
{
    dispatch_source_t _mytimer;
}

dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    _mytimer = timer;
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 0.2 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"++++++++++++++++++++%@", [NSThread currentThread]);
    });
// 开启定时器
    dispatch_resume(timer);

当我们需要停止定时器的时候,执行该方法:
注意:当在停止后不能再调用dispatch_resume方法打开定时器,否则导致程序奔溃。

dispatch_source_cancel(_mytimer);

NSTimer


NSTimer定时器使用:
这里timer同样是一个局部变量,但是在函数体走完只有timer并没有销毁,所以定时器是可以正常工作,内部应该已经对timer有了一个强引用

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(test) userInfo:nil repeats:YES];
    // 执行定时器
    [timer fire];

// 定时器方法
- (void)test
{
    NSLog(@"-------------");
}

同样的,当需要停止定时器的时候,调用该方法

- (void)invalidate;

如果需要再次开启定时器的时候,不可以在对该timer执行fire方法开启,而是需要充新创建一个新的定时器进行赋值

- (IBAction)switchAction:(UISwitch *)sender {
    
    if (sender.isOn) { // 打开
        
    self.timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(test) userInfo:nil repeats:YES];
    [self.timer fire];
        
    }else{ // 关闭
        [self.timer invalidate];
    }
}

NSTimer定时器受NSRunloop影响:当timer被创建了以后,timer被添加到当前RunLoop的NSDefaultRunLoopMode模式。而如果当前线程就是主线程,也就是UI线程时,某些UI事件,比如UIScrollView的拖动操作,会将Run Loop切换成NSEventTrackingRunLoopMode模式,在这个过程中,默认的NSDefaultRunLoopMode模式中注册的事件是不会被执行的

为了设置一个不被UI干扰的Timer,我们需要手动创建一个Timer,然后使用NSRunLoop的addTimer:forMode:方法来把Timer按照指定模式加入到Run Loop中。这里使用的模式是:NSRunLoopCommonModes,这个模式等效于NSDefaultRunLoopMode和NSEventTrackingRunLoopMode的结合

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(test) userInfo:nil repeats:YES];
    // 执行定时器
    [timer fire];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:UITrackingRunLoopMode];

NSRunLoop


NSRunLoop的特点

  • 每条线程都有唯一的一个与之对应的RunLoop对象
  • 主线程的RunLoop已经自动创建好了,子线程的RunLoop需要主动创建
  • RunLoop在第一次获取时创建,在线程结束时销毁

NSRunLoop的模式

如果需要切换Mode,只能退出Loop,再重新指定一个Mode进入

RunLoop只能选择一个Mode启动,如果当前Mode中没有任何Source(Sources0、Sources1)、Timer,那么就直接退出RunLoop

系统默认注册了5个Mode:
kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行

UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响

UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用

GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到

kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode

你可能感兴趣的:(定时器 与 NSRunLoop)