定时器相关(NStimer、CADisplayLink、dispatch_source_t)

NSTimer

  • NSTimer计时器,会被UIScrollView 打断,解决办法
    1
    2
    3
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
    self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
  • __weakblock能解除循环引用
    1
    2
    3
    4
    __weak typeof(self) weakSelf = self;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
    [weakSelf test];
    }];
  • NSProxy解决循环引用,效率很高,因为不经过Runtime的,消息发送,消息动态解析,去缓存中查找等流程,直接通过消息转发。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    #import <Foundation/Foundation.h>
    @interface YZProxy : NSProxy
    + (instancetype)proxyWithTarget:(id)target;
    @property (weak, nonatomic) id target;
    @end

    #import "YZProxy.h"
    @implementation YZProxy

    + (instancetype)proxyWithTarget:(id)target {
    // NSProxy对象不需要调用init,因为它本来就没有init方法
    YZProxy *proxy = [YZProxy alloc];
    proxy.target = target;
    return proxy;
    }

    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [self.target methodSignatureForSelector:sel];
    }

    - (void)forwardInvocation:(NSInvocation *)invocation {
    [invocation invokeWithTarget:self.target];
    }
    @end
    // 使用的时候,如下就可以了。
    - (void)viewDidLoad {
    [super viewDidLoad];
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:[YZProxy proxyWithTarget:self] selector:@selector(test) userInfo:nil repeats:YES];
    }

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

CADisplayLink

  • CADisplayLink是用于同步屏幕刷新频率的计时器,CADisplayLink和NSTimer一样也会导致循环引用,解决办法和前面的NSTimer一样。

dispatch_source_t

  • NSTimer依赖于RunLoop,如果RunLoop的任务过于繁重,可能会导致NSTimer不准时,这时可以用GCD的定时器。因为是直接跟内核挂钩的,跟RunLoop没有关系,所以也不会有常见的RunLoop模式的改变而导致定时器的暂停等问题
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     // 主队列
    dispatch_queue_t queue = dispatch_get_main_queue();

    // 创建定时器
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

    // 设置时间
    uint64_t start = 2.0; // 2秒后开始执行
    uint64_t interval = 1.0; // 每隔1秒执行
    dispatch_source_set_timer(timer,dispatch_time(DISPATCH_TIME_NOW, start * NSEC_PER_SEC), interval * NSEC_PER_SEC, 0);

    // 设置回调
    dispatch_source_set_event_handler(timer, ^{
    NSLog(@"1111");
    });

    // 启动定时器
    dispatch_resume(timer);
    self.GCDtimer = timer;