前言

在之前的一篇内容RxSwift学习--核心逻辑初探中,曾列举了一些使用RxSwift优势的小例子,其中关于Timer定时器的例子,在RxSwift中创建的定时器并不受RunLoop的影响,至于为什么不受RunLoop的影响,具体原因还有待分析。

Timer的创建方式

1. NSTimer

相信大家在Object-C中都有使用过NSTimer,其创建方式在Swift中比较类似的

(1)第一种写法

func testTimer(){timer = Timer.init(timeInterval: 1, target: self, selector: #selector(timerFire), userInfo: nil, repeats: true)RunLoop.current.add(timer, forMode: .common)
}@objc func timerFire(){print("If i know what love is,it is because of you!")
}
复制代码

(2)第二种写法

timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { (timer) inprint("If i know what love is,it is because of you!")
})
RunLoop.current.add(timer, forMode: .common)
复制代码

这两种方式创建的Timer都会受RunLoop的影响,它的准确性依赖于RunLoop的状态。

2. Dispatch Source Timer

Dispatch Source Timer 是一种与 Dispatch Queue 结合使用的定时器,当需要在后台 queue 中定期执行任务的时候,使用 Dispatch Source Timer 要比使用 NSTimer 更加自然,也更加高效,且无需在 main queue 和后台 queue 之前切换.

func testGCD(){gcdTimer = DispatchSource.makeTimerSource()gcdTimer?.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.seconds(1))gcdTimer?.setEventHandler(handler: {print("You are not brave,no one is strong for you.")})gcdTimer?.resume()//gcdTimer?.suspend()//gcdTimer?.cancel()//gcdTimer = nil}
复制代码

Dispatch Source Timer是不受RunLoop状态的影响的。

注意1: gcdTimer?.cancel() 则是真正意义上的取消 Timer,被取消之后如果想再次执行 Timer,只能重新创建新的 Timer。这个过程类似于对 NSTimer 执行 invalidate.

注意2: 关于取消 Timer,gcdTimer?.suspend() 之后的 Timer,是不能被释放的(gcdTimer = nil),会引起崩溃,因为使用 gcdTimer?.suspend() 时,Timer 本身的实例需要一直保持,但是使用 gcdTimer?.cancel() 则没有这个崩溃问题。

3. CADisplayLink

CADisplayLink是用于同步屏幕刷新频率的计时器.

func testCAD(){cadTimer = CADisplayLink(target: self, selector: #selector(timerFire))cadTimer?.preferredFramesPerSecond = 1cadTimer?.add(to: RunLoop.current, forMode: .common)// cadTimer?.remove(from: RunLoop.current, forMode: .common)
}@objc func timerFire(){print("Like is the choice, love is only you!")
}复制代码

CADisplayLink同样会受RunLoop的状态的影响。

注意: cadTimer?.remove(from: RunLoop.current, forMode: .common)将接收者从给定的模式中移除,这个方法会对计时器进行隐式的release,在调用.remove(),需要做判断,如果当期计时器不在RunLoop的话,会出现野指针的crash.

RxSwift中的Timer

1.Timer的创建

先来写一个小例子试一下:

func RxTimer(){rxtimer = Observable<Int>.timer(1, period: 3, scheduler: MainScheduler.instance)rxtimer.subscribe(onNext: { (num) inprint("Happy in pain \(num)")}).disposed(by: disposeBag)}
复制代码

运行之后可以看到rxtimer完全不受RunLoop的状态的影响。

timerObservable的一个操作符,(Observable有很多操作符,后续会不断补充)

public static func timer(_ dueTime: RxTimeInterval, period: RxTimeInterval? = nil, scheduler: SchedulerType) -> RxSwift.Observable<Self.E>
复制代码

参数dueTime:初始延时(订阅开始和发送第一个元素值之间的时间段)

参数period:每次发送的时间间隔

参数scheduler:所在调度器,类似于线程

2.源代码分析

下面开始分析原因: 首先来到RxSwift下的Timer.swift文件,找到Timer类,源码如下:

final private class Timer<Element: RxAbstractInteger>: Producer<Element> {fileprivate let _scheduler: SchedulerTypefileprivate let _dueTime: RxTimeIntervalfileprivate let _period: RxTimeInterval?init(dueTime: RxTimeInterval, period: RxTimeInterval?, scheduler: SchedulerType) {self._scheduler = schedulerself._dueTime = dueTimeself._period = period}override func run<Observer: ObserverType>(_ observer: Observer, cancel: Cancelable) -> (sink: Disposable, subscription: Disposable) where Observer.Element == Element {if self._period != nil {let sink = TimerSink(parent: self, observer: observer, cancel: cancel)let subscription = sink.run()return (sink: sink, subscription: subscription)}else {let sink = TimerOneOffSink(parent: self, observer: observer, cancel: cancel)let subscription = sink.run()return (sink: sink, subscription: subscription)}}
}
复制代码

看过RxSwift核心逻辑的应该知道,继承于Producer的类,必然会进入到当前类run()方法,(具体逻辑请参考RxSwift核心逻辑);

可以看到当前类的run()方法,会创建一个TimerSink,然后这个TimerSink执行了自己的sink.run()方法,那么再次跟进去TimerSink类的源码:

final private class TimerSink<Observer: ObserverType> : Sink<Observer> where Observer.Element : RxAbstractInteger  {typealias Parent = Timer<Observer.Element>private let _parent: Parentprivate let _lock = RecursiveLock()init(parent: Parent, observer: Observer, cancel: Cancelable) {self._parent = parentsuper.init(observer: observer, cancel: cancel)}func run() -> Disposable {return self._parent._scheduler.schedulePeriodic(0 as Observer.Element, startAfter: self._parent._dueTime, period: self._parent._period!) { state inself._lock.lock(); defer { self._lock.unlock() }self.forwardOn(.next(state))return state &+ 1}}
}
复制代码

可以看到TimerSinkrun()方法会返回self.Timer._scheduler.schedulePeriodic()方法,那么就再次跟进去这个方法;这里在跟方法的时候,可能不知道跟进去哪一个?

在最初声明这个Timer的时候,我们在传参数的时候,对参数scheduler:传入的是MainScheduler.instance,那么我们就跟进去ConcurrentMainScheduler.schedulePeriodic()这个方法;

public func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {return self._mainScheduler.schedulePeriodic(state, startAfter: startAfter, period: period, action: action)}
复制代码

进去之后发现这只是一个过渡方法,那么久继续跟进;

public func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {return self.configuration.schedulePeriodic(state, startAfter: startAfter, period: period, action: action)}
复制代码

发现还是过渡方法,那就再次跟进;

func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {let initial = DispatchTime.now() + startAftervar timerState = statelet timer = DispatchSource.makeTimerSource(queue: self.queue)timer.schedule(deadline: initial, repeating: period, leeway: self.leeway)var timerReference: DispatchSourceTimer? = timerlet cancelTimer = Disposables.create {timerReference?.cancel()timerReference = nil}timer.setEventHandler(handler: {if cancelTimer.isDisposed {return}timerState = action(timerState)})timer.resume()return cancelTimer}
复制代码

最终来到schedulePeriodic()这个方法的具体实现,可以看到这句let timer = DispatchSource.makeTimerSource(queue: self.queue)这里就是初始化了一个Dispatch Source Timer

然后看一下timer.setEventHandler()方法,在这个方法里有这样一句代码timerState = action(timerState)就是一直在改变timer的状态,那么这个action是什么呢?可以看到,这个action传入的是一个尾随闭包,那么这个闭包是什么呢?视线再次回到TimerSink类的源码:

{ state inself._lock.lock(); defer { self._lock.unlock() }self.forwardOn(.next(state))return state &+ 1}
复制代码

这个就是action传入的尾随闭包。看到这里会发现,每次执行action()方法的时候,也就是每次会调用这个闭包,在这个闭包内部会执行self.forwardOn(.next(state)),

self.forwardOn(.next(state))这个方法会调用self._observer.on(event),这个self._observer.on(event)方法就会把的event传入到.subscribe()订阅方法中,订阅方法中的AnonymousObserver的闭包会根据传入的event枚举值调用onNext(),这时.subscribe()订阅方法中的onNext:就能够等到响应了。(这里具体的逻辑请参考上篇内容,由于代码过多,在这里就不再次赘述了)

总结

至此,关于RxSwift中的Timer就写到这里,可以看到在Timer的源码中,是创建了一个Dispatch Source Timer,并且不断的调用timer的状态,让订阅方法中的闭包得到响应。关于Timer的研究还是不够深入,后续会再更新。

转载于:https://juejin.im/post/5d3dac3d6fb9a07f014f3f13

RxSwift学习插曲--Timer补充内容相关推荐

  1. RxSwift学习--核心逻辑再探

    前言 通过上一篇内容RxSwift学习--核心逻辑初探,对RxSwift有了些初步的认知,下面通过源码来看一下RxSwift到底有多骚 RxSwift核心逻辑再探 先把上篇中的例子代码搬过来: //第 ...

  2. 【体系结构】有关Oracle SCN知识点的整理--补充内容

    [体系结构]有关Oracle SCN知识点的整理--补充内容 小麦苗自己整理的内容参考:[体系结构]有关Oracle SCN知识点的整理  http://blog.itpub.net/26736162 ...

  3. 第二十五章补充内容 3 assert()宏

    //第二十五章补充内容 3 assert()宏 //有的编译器还提供了assert()宏,这个宏在许多书中被翻译为断言,它的作用是当assert()的参数为真时,返回真,假如参数值为假,那么它将执行某 ...

  4. 第二十五章补充内容 5 不能为0的变量

    // 第二十五章补充内容 5 不能为0的变量 /*#define DEBUG #include <iostream> #include <string> using names ...

  5. 安卓学习笔记36:内容提供者

    文章目录 一.内容提供者 (一)概述 (二)作用 二.案例演示 - 显示系统联系人 (一)运行效果 (二)涉及知识点 (三)实现步骤 1.创建安卓应用[DisplayContacts] 2.将背景图片 ...

  6. 【汇编语言与计算机系统结构笔记20】补充内容:可定制处理器指令集

    本次笔记内容: 31.补充内容--可定制处理器指令集-1 32.补充内容--可定制处理器指令集-2 注:我找到了对应内容的课件,请见我于GitHub的CS笔记仓库. 本节课对应幻灯片:汇编语言程序设计 ...

  7. [2022]李宏毅深度学习与机器学习课程内容总结

    [2022]李宏毅深度学习与机器学习课程内容总结 课程感受 第一讲必修 ML的三个步骤 第一讲选修 深度学习发展趋势 反向传播 从线性模型到神经网络 为什么要用正则化技术 为什么分类是用交叉熵损失函数 ...

  8. php写入文件内容方法,学习php写入文件内容的方法

    在PHP网站开发中,存储数据通常有两种方式,一种以文本文件方式存储,比如txt文件,一种是以数据库方式存储,比如Mysql,相对于数据库存储,文件存储并没有什么优势,但是文件读写操作在基本的PHP开发 ...

  9. Rxswift学习之(一)函数响应式编程思想

    Rxswift学习之(一)函数响应式编程思想 1. 函数响应式编程思想必备基本概念简介 2. iOS中三种编程思想:链式.函数式和响应式编程 2.1 链式编程 2.2 函数式编程 2.3 响应式编程 ...

最新文章

  1. 机器学习(14)逻辑回归(实战) -- 癌症分析
  2. 基于JDK1.8---HashMap源码分析
  3. Waiting for Debugger
  4. java知识积累——单元测试和JUnit(二)
  5. 【LeetCode从零单排】No27.Remove Element
  6. [转]全面了解setjmp与longjmp的使用
  7. 深度学习特征归一化方法——BN、LN、IN、GN
  8. python截取指定字符串_python 正则匹配获取指定多个词的在字符串(句子/段落)索引位置...
  9. 学习《让UpdatePanel支持文件上传》系列文章的相关链接
  10. 叉姐训练目录,好好搞搞,两个月要搞定哦
  11. 数据库的增删改查基本操作
  12. bzoj1984 月下“毛景树”
  13. “死神”百草枯:每年超万人中毒 没有解药
  14. 柯美服务器处理文件慢,处理打印机在打印文件时打印速度过慢的原因 看完你就知道了...
  15. 【通信】Matlab实现多同步压缩变换
  16. 服务器没有立即响应请求,服务器没有及时响应或控制请求
  17. KF、EKF、ESKF的区别与联系
  18. 新网站建设的完整步骤
  19. Linux学习:第一天_笔记
  20. 架构师进阶之路,JAVA架构师面试题

热门文章

  1. Hive分析函数--row_number 的用法
  2. (转)Redis上踩过的一些坑-美团
  3. 关于python学习,最系统的学习网站看这里
  4. Python浅谈gevent实现协程
  5. ExtJS的extend(Ext Designer的使用)
  6. Python《搞事情==蜂-鸟-图-片(二)》
  7. 深度学习《CGAN新认识》
  8. 《BERT:Pre-training of Deep Bidirectional Transformers for Language Understanding》
  9. Topic model相关文章总结
  10. org.json的使用详解