『ios』进阶

『ios』YYTimer 源码分析

2018-08-23  本文已影响27人  butterflyer

yy系列一直都在用,最近打算抽出时间来仔细阅读下大神的源码。因为今天第一眼就看到yytimer了,所以第一个由他开始吧。

首先看介绍

 YYTimer is a thread-safe timer based on GCD. It has similar API with `NSTimer`.
 YYTimer object differ from NSTimer in a few ways:
 
 * It use GCD to produce timer tick, and won't be affected by runLoop.
 * It make a weak reference to the target, so it can avoid retain cycles.
 * It always fire on main thread.

翻译来,就是类似于nstimer的api,由gcd生成的线程安全的api.
不受runloop影响,target是由weak修饰,不会引起循环引用,在主线程。

先看.h文件

@interface YYTimer : NSObject

+ (YYTimer *)timerWithTimeInterval:(NSTimeInterval)interval
                            target:(id)target
                          selector:(SEL)selector
                           repeats:(BOOL)repeats;//构造方法

- (instancetype)initWithFireTime:(NSTimeInterval)start
                        interval:(NSTimeInterval)interval
                          target:(id)target
                        selector:(SEL)selector
                         repeats:(BOOL)repeats NS_DESIGNATED_INITIALIZER;

@property (readonly) BOOL repeats;//是否重复
@property (readonly) NSTimeInterval timeInterval;//时间间隔
@property (readonly, getter=isValid) BOOL valid;//
- (void)invalidate;
- (void)fire;
@end

继续往下面看
先从构造方法开始看

+ (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];
}

两个构造方法都指向下面的方法,所以这个方法才是重点

{
    BOOL _valid;
    NSTimeInterval _timeInterval;
    BOOL _repeats;
    __weak id _target;   //弱引用
    SEL _selector;
    dispatch_source_t _source;
    dispatch_semaphore_t _lock;
}

- (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;   
    _lock = dispatch_semaphore_create(1);//在这里创建了一个全局的信号量
    _source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());//全局的source  
    dispatch_source_set_timer(_source, dispatch_time(DISPATCH_TIME_NOW, (start * NSEC_PER_SEC)), (interval * NSEC_PER_SEC), 0); //创建时间计时器,这里应该就是不受runloop影响的原因
    dispatch_source_set_event_handler(_source, ^{[_self fire];});//触发的事件
    dispatch_resume(_source);//执行source
    return self;
}

上面用到了gcd中的两个知识,dispatch_source和 信号量 dispatch_semaphore
之前在写时间循环的时候用过dispatch_source这个函数.

- (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
}

.m文件中有个宏

define LOCK(...) dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \

VA_ARGS;
dispatch_semaphore_signal(_lock);
针对信号量,形成锁的宏

- (NSTimeInterval)timeInterval {
    LOCK(NSTimeInterval t = _timeInterval) return t;
}
- (BOOL)repeats {
    LOCK(BOOL repeat = _repeats); return repeat;
}

其中对于VA_ARGS的解释

__VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错, 你可以试试

我的理解,这里的VA_ARGS,就是穿进去的赋值操作 NSTimeInterval t = _timeInterval 和
BOOL repeat = _repeats,目的是形成锁。

ios自习室欢迎进入,一起学习一起进步。

IMG_7291.JPG
上一篇下一篇

猜你喜欢

热点阅读