YYTimer的学习

作者: _阿南_ | 来源:发表于2017-09-26 16:51 被阅读62次
    图片来之网络

    定时器用了那么多年,从来没有想过如何自己实现,除了for循环加上线程的sleep,没有想到更好的方法。等看到代码

    __weak typeof(self) _self = self;
        _lock = dispatch_semaphore_create(1);
        _source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
        dispatch_source_set_timer(_source, dispatch_time(DISPATCH_TIME_NOW, (start * NSEC_PER_SEC)), (interval * NSEC_PER_SEC), 0);
        dispatch_source_set_event_handler(_source, ^{[_self fire];});
        dispatch_resume(_source);
    

    才明白,多线程那么古老的实现啊。

    信号量

    代码中_lock = dispatch_semaphore_create(1);是使用了信号量semaphore。
    在GCD中有三个函数是semaphore的操作,分别是:

    • dispatch_semaphore_create   创建一个semaphore
    • dispatch_semaphore_signal   发送一个信号
    • dispatch_semaphore_wait    等待信号
    - (void)dispatch
    {
        dispatch_group_t group = dispatch_group_create();
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
        for (int i = 0; i < 100; i++) {
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            dispatch_group_async(group, queue, ^{
                NSLog(@"i is %i", i);
                sleep(2);
    
                dispatch_semaphore_signal(semaphore);
            });
        }
    
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
        NSLog(@"结果");
    }
    

    创建一个初始值为10的semaphore,可以实现最多并发上限为10. 使用dispatch_semaphore_wait方法来阻止并发,如果信号量的值为0则一直等待。

    • 调用dispatch_semaphore_wait时表示,需要使用资源,semaphore的value 减一。
    • 调用dispatch_semaphore_signal时表示,需要放放资源,semaphore的value加以。
    • 所以这2个方法的调用需要成对出现,不然会导致线程被无限的阻塞。

    dispatch源

    _source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    

    创建一个定时器的dispatch源,DISPATCH_SOURCE_TYPE_TIMER为定时器类型。

    定时器

    dispatch_source_set_timer(_source, dispatch_time(DISPATCH_TIME_NOW, (start * NSEC_PER_SEC)), (interval * NSEC_PER_SEC), 0);
    

    创建一个定时器,设置第一次触发的时间,间隔时间。

    dispatch_source_set_event_handler(_source, ^{[_self fire];});
    

    设置响应dispatch源事件的block,在分派源指定的队列上运行.

    dispatch_resume(_source);
    

    在dispatch源创建后默认是挂起的,需要用dispatch_resume函数来恢复监听。
    定时器就创建完成了,主要使用GCD提供的方法来实现。

    保证定时任务每次执行一条

    - (void)invalidate {
        dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
        if (_valid) {
            dispatch_source_cancel(_source);
            _source = NULL;
            _target = nil;
            _valid = NO;
        }
        dispatch_semaphore_signal(_lock);
    }
    
    - (void)fire {
        if (!_valid) return;
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
        id target = _target;
        if (!target) {
            dispatch_semaphore_signal(_lock);
            [self invalidate];
        } else {
            dispatch_semaphore_signal(_lock);
            [target performSelector:_selector withObject:self];
            if (!_repeats) {
                [self invalidate];
            }
        }
    #pragma clang diagnostic pop
    }
    

    每次调用方法由定时器触发。因为semaphore初始化值为1,所以资源最多为1,保证每次只能执行一条。

    // END

    相关文章

      网友评论

      • 经文纬武:请问 为什么我用YYTimer 的方法只执行一次。
        -(void)configureTimer{
        //开启定时器, 1 上报车队成员最新位置 2,获取成员位置 3
        YYTimer *timer=[YYTimer timerWithTimeInterval:3 target:self selector:@selector(timerMethod) repeats:YES];
        [timer fire];
        }

        #pragma mark 定时器方法
        -(void)timerMethod{
        ZSLOG(@"定时器方法");

        }
        经文纬武:@_阿南_ 嗯嗯,解决了。谢谢!!!
        _阿南_:把 YYTimer *timer 设置为实例变量。
      • Mr卿:呼呼

      本文标题:YYTimer的学习

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