YYTimer源码阅读

YYTimer是一个基于GCD的线程安全的计时器,它的API和NSTimer类似。
有以下几种不同点:

  • 基于GCD,不会被runloop影响
  • 避免循环引用
  • 总是运行在主线程

如下三个GCD函数实现YYTimer,利用信号量控制锁保证了线程安全。

(1)dispatch_semaphore_create的声明为:

  dispatch_semaphore_t  dispatch_semaphore_create(long value);

  传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量。

  值得注意的是,这里的传入的参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。

  (关于信号量,我就不在这里累述了,网上很多介绍这个的。我们这里主要讲一下dispatch_semaphore这三个函数的用法)。

 

(2)dispatch_semaphore_signal的声明为:

  long dispatch_semaphore_signal(dispatch_semaphore_t dsema)

  这个函数会使传入的信号量dsema的值加1;(至于返回值,待会儿再讲)

 

 (3) dispatch_semaphore_wait的声明为:

  long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

  这个函数会使传入的信号量dsema的值减1;

  这个函数的作用是这样的,如果dsema信号量的值大于0,该函数所处线程就继续执行下面的语句,并且将信号量的值减1;

  如果desema的值为0,那么这个函数就阻塞当前线程等待timeout(注意timeout的类型为dispatch_time_t,

  不能直接传入整形或float型数),如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,

  且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1。

  如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句。


//
//  YYTimer.m
//  YYKit 
//
//  Created by ibireme on 15/2/7.
//  Copyright (c) 2015 ibireme.
//
//  This source code is licensed under the MIT-style license found in the
//  LICENSE file in the root directory of this source tree.
//

#import "YYTimer.h"
#import 

#define LOCK(...) dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \
__VA_ARGS__; \
dispatch_semaphore_signal(_lock);


@implementation YYTimer {
    BOOL _valid;
    NSTimeInterval _timeInterval;
    BOOL _repeats;
    __weak id _target;
    SEL _selector;
    dispatch_source_t _source;
    dispatch_semaphore_t _lock;
}

+ (YYTimer *)timerWithTimeInterval:(NSTimeInterval)interval
                            target:(id)target
                          selector:(SEL)selector
                           repeats:(BOOL)repeats {
    return [[self alloc] initWithFireTime:interval interval:interval target:target selector:selector repeats:repeats];
}

- (instancetype)init {
    @throw [NSException exceptionWithName:@"YYTimer init error" reason:@"Use the designated initializer to init." userInfo:nil];
    return [self initWithFireTime:0 interval:0 target:self selector:@selector(invalidate) repeats:NO];
}

- (instancetype)initWithFireTime:(NSTimeInterval)start
                        interval:(NSTimeInterval)interval
                          target:(id)target
                        selector:(SEL)selector
                         repeats:(BOOL)repeats {
    self = [super init];
    _repeats = repeats;
    _timeInterval = interval;
    _valid = YES;
    _target = target;
    _selector = selector;
    
    __weak typeof(self) _self = self;
    
    // 创建一个值为1的信号量,用来当线程锁
    _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);
    return self;
}

- (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 {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
    id target = _target;
    if (!_repeats || !target) {
        dispatch_semaphore_signal(_lock);
        [self invalidate];
    } else {
        dispatch_semaphore_signal(_lock);
        [target performSelector:_selector withObject:self];
    }
#pragma clang diagnostic pop
}

- (BOOL)repeats {
    LOCK(BOOL repeat = _repeats); return repeat;
}

- (NSTimeInterval)timeInterval {
    LOCK(NSTimeInterval t = _timeInterval) return t;
}

- (BOOL)isValid {
    LOCK(BOOL valid = _valid) return valid;
}

- (void)dealloc {
    [self invalidate];
}

@end

你可能感兴趣的:(YYTimer源码阅读)