美文网首页
定时器 与 NSRunLoop

定时器 与 NSRunLoop

作者: coder_hong | 来源:发表于2017-04-09 14:30 被阅读192次

    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

          本文链接:https://www.haomeiwen.com/subject/xgfdattx.html