NSTimer

概述

NSTimerFoundation结构中的一个类,用于创立守时器,能够在指定的时刻距离内履行指定的使命。NSTimerRunLoop的办法作业,能够将其加入RunLoop的特定形式中,然后守时器能够在指定的时刻距离内持续作业

调用办法

办法1

// 以该办法创立的守时器以默认办法增加到当前线程runloop中,无需手动增加 
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) { 
    NSLog(@"%s, %d", __func__, __LINE__); 
}]; 
// 之所以指定该守时器地点的Mode为NSRunLoopCommonModes,是为了确保守时器能够在不同的RunLoop Mode下正常运行
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 
// 中止守时器 
[timer invalidate];

办法2

// 以该办法创立的守时器需求手动增加到RunLoop才干正常履行 
NSTimer *timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(test:) userInfo:@"2" repeats:YES]; 
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; 
-(void)test: (NSTimer *)content { 
    NSLog(@"%s, %d, %@", __func__, __LINE__, [content userInfo]); 
} 
// 中止守时器 
[timer invalidate];

运用圈套

  • NSTimer的作业办法是基于RunLoop的,因而需求将其加入RunLoop才干收效。假如不加入RunLoop,守时器将无法发动
  • 在运用NSTimer时要留意循环引证问题,应该运用弱引证或者block来防止循环引证,也可经过NSProxy创立一个临时Target
  • 在多线程中运用NSTimer,需求留意线程安全的问题,能够考虑运用GCD守时器或者NSRunLoopperformSelector:afterDelay: 办法等代替
  • 当守时器不再运用时,需求将其从RunLoop中移除,防止内存走漏和无用功耗,且其创立与吊销必须在同一个线程操作,不能跨越线程操作
  • NSTimer依赖于Runloop,假如Runloop的使命过于深重,可能会导致NStimer不按时

CADisplayLink

概述

CADisplayLinkCore Animation结构中的一个类,用于创立一个与显现器刷新频率相同的计时器,如每秒 60 次(屏幕刷新率为 60Hz)调用一次指定办法。CADisplayLink能够直接和Core Animation进行结合,用于完成动画烘托、图形制作等操作,并且能够在多个RunLoop Modes中履行,非常灵敏。

运用场景

  • 实时数据展示:比如在股票行情中,实时制作股票价格图表
  • 游戏开发:比如用于精灵动画的接连播映
  • 视频录制:实时处理视频帧数据,完成视频的录制和处理
  • 音频波形分析:实时制作音频波形图,显现音频数据变化

调用办法

// 需求将CADisplayLink目标增加到runLoop中,selector就能被周期性调用 
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(test:)]; 
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

运用圈套

  • CADisplayLink的使命办法需求在RunLoop中运行,因而需求将其加入指定的RunLoop Mode
  • CADisplayLink显现器刷新率为 60Hz,因而它的使命办法最好不要履行过于耗时的操作,以防止影响界面流畅性
  • 在运用CADisplayLink时要留意循环引证问题,应该运用弱引证或者block来防止循环引证
  • CADisplayLink不再运用时,需求手动中止它的作业,防止内存走漏和无用功耗

GCD

+ (NSString *)executeTask:(task)task
         start:(NSTimeInterval)start
        interval:(NSTimeInterval)interval
        repeats:(BOOL)repeats
         async:(BOOL)async {
  if (!task || start < 0 || (interval <= 0 && repeats) ) return nil;
 
  // 行列
  dispatch_queue_t queue = async ? dispatch_get_global_queue(0, 0) : dispatch_get_main_queue();
  // 创立守时器
  dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
  // 设置时刻
  // 精准度:一般为0,假如对守时操作要求不精确,只要在一段时刻内履行就能够,那么能够把精准度设置的大一点,例如10,这样会进步程序功能
  dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, interval * NSEC_PER_SEC, 0);
  // 加锁
  dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
  // 守时器唯一标识
  NSString *name = [NSString stringWithFormat:@"%ld", timers_.count];
  // 存放到字典中
  timers_[name] = timer;
  // 解锁
  dispatch_semaphore_signal(semaphore_);
  // 设置回调
  dispatch_source_set_event_handler(timer, ^{
    task();
    // 不重复的使命
    if (!repeats) {
      [self cancelTask:name];
    }
  });
  // 发动守时器
  dispatch_resume(timer);
  return name;
}

长处

GCD守时器实际上是运用了dispatch源(dispatch source),dispatch源监听系统内核目标并处理。dispatch相似出产者消费者形式,经过监听系统内核目标,在出产者出产数据后主动告诉相应的dispatch行列履行,后者充当消费者。经过系统级调用,更加精准