定时器

参考文档1
参考文档2

1.NSTimer

创建NSTimer

//常用方法
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;

//常用方法
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti target:(id)t selector:(SEL)s userInfo:(nullable id)ui repeats:(BOOL)rep
  • scheduledTimerWithTimeInterval 方法相比timerWithTimeInterval,不仅初始化了一个timer对象,同时还将该对象放入到当前的Runloop中,而后者需要手动将timer放入runloop。最重要的是NSTimer只有放到runloop才会生效。(runloop后面会专门写一下)。
-(void)addTimer:(NSTimer *)aTimer forMode:(NSString *)mode
  • timer定时器并不是一个精确运行的,精确运行需要用GCDtimer
  • 正常情况下tableView等滚动视图添加timer后,视图滚动后
    RunLoop会将mode切换成TrackingRunLoopMode 不会调用timer
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(updateUI) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
//NSRunLoopCommonModes的意思为,定时器可以运行在标记为common modes模式下。
//具体包括两种: kCFRunloopDefaultMode  和   UITrackingRunloopMode。
[timer fire];

暂停/启动NSTimer

//启动,常用方法创建的直接就启动了
[self.timer setFireDate:[NSDate distantPast]];
//暂停
[self.timer setFireDate:[NSDate distantFuture]];

释放NSTimer

释放写在viewWillDisappear!!!!!
[self.timer invalidate];//将timer停止并移出runloop
self.timer = nil;
NSTimer在ARC下
  • 初始化后放到runloop后,除了ViewController外,iOS系统也强引用NSTimer对象。

  • 将timer对象置nil后,会解除VC对timer的中引用。但iOS系统依然对NSTimer和VC存在强引用关系。(因为并未解除系统对timer的强引用,并且系统为了实现NSTimer而对VC继续保持引用)。所以先对timer 执行invalidate(解系统对timer的强引用),再置nil(解除VC对timer引用)。

  • 由于系统和VC对timer的双重引用,导致dealloc 方法无法执行,所以理想的方法是释放代码写在viewWillDisappear方法中。

2.GCD

dispatch source是一个监视某些类型事件的对象。
当这些事件发生时,它自动将一个block放入一个dispatch queue的执行例程中。
    /**
     *  创建dispatch源
     *
     *  @param DISPATCH_SOURCE_TYPE_TIMER 事件源的类型
     *  @param 0                          <#0 description#>
     *  @param 0                          <#0 description#>
     *  @param dispatch_get_main_queue    在哪个线程上执行
     *
     *  @return dispatch_source_t
     */
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    /**
     * @param start 控制计时器第一次触发的时刻
     * @param interval 每隔多长时间执行一次
     * @param leeway 误差值,0表示最小误差,值越小性能消耗越大
     */
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);

    //事件处理的回调
    dispatch_source_set_event_handler(timer, ^{
        
    });

    // Dispatch source启动时默认状态是挂起的,创建完毕之后得主动恢复,
    //否则事件不会被传递,也不会被执行
    dispatch_resume(timer);
    //取消定时器
    dispatch_cancel(timer);

3.CADisplayLink

CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(disPlay)];
//与NSTimer不同, 需要手动添加到RunLoop中, 如果添加的模式是NSDefaultRunLoopMode, 则scrollview滚动时, 会阻碍定时器的执行。
//如果是NSRunLoopCommonModes, 则不会阻碍。
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//设置刷新频率, 默认是每秒60帧, 如果设置成60就每秒执行一次
//但是实际测试却发现,设置60是2秒执行一次, 设置成30才是每秒执行一次。但精度依然比NSTimer高
    displayLink.frameInterval = 60;

你可能感兴趣的:(定时器)