RxSwift学习插曲--Timer补充内容
前言
在之前的一篇内容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
的状态的影响。
timer
是Observable
的一个操作符,(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}}
}
复制代码
可以看到TimerSink
的run()
方法会返回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补充内容相关推荐
- RxSwift学习--核心逻辑再探
前言 通过上一篇内容RxSwift学习--核心逻辑初探,对RxSwift有了些初步的认知,下面通过源码来看一下RxSwift到底有多骚 RxSwift核心逻辑再探 先把上篇中的例子代码搬过来: //第 ...
- 【体系结构】有关Oracle SCN知识点的整理--补充内容
[体系结构]有关Oracle SCN知识点的整理--补充内容 小麦苗自己整理的内容参考:[体系结构]有关Oracle SCN知识点的整理 http://blog.itpub.net/26736162 ...
- 第二十五章补充内容 3 assert()宏
//第二十五章补充内容 3 assert()宏 //有的编译器还提供了assert()宏,这个宏在许多书中被翻译为断言,它的作用是当assert()的参数为真时,返回真,假如参数值为假,那么它将执行某 ...
- 第二十五章补充内容 5 不能为0的变量
// 第二十五章补充内容 5 不能为0的变量 /*#define DEBUG #include <iostream> #include <string> using names ...
- 安卓学习笔记36:内容提供者
文章目录 一.内容提供者 (一)概述 (二)作用 二.案例演示 - 显示系统联系人 (一)运行效果 (二)涉及知识点 (三)实现步骤 1.创建安卓应用[DisplayContacts] 2.将背景图片 ...
- 【汇编语言与计算机系统结构笔记20】补充内容:可定制处理器指令集
本次笔记内容: 31.补充内容--可定制处理器指令集-1 32.补充内容--可定制处理器指令集-2 注:我找到了对应内容的课件,请见我于GitHub的CS笔记仓库. 本节课对应幻灯片:汇编语言程序设计 ...
- [2022]李宏毅深度学习与机器学习课程内容总结
[2022]李宏毅深度学习与机器学习课程内容总结 课程感受 第一讲必修 ML的三个步骤 第一讲选修 深度学习发展趋势 反向传播 从线性模型到神经网络 为什么要用正则化技术 为什么分类是用交叉熵损失函数 ...
- php写入文件内容方法,学习php写入文件内容的方法
在PHP网站开发中,存储数据通常有两种方式,一种以文本文件方式存储,比如txt文件,一种是以数据库方式存储,比如Mysql,相对于数据库存储,文件存储并没有什么优势,但是文件读写操作在基本的PHP开发 ...
- Rxswift学习之(一)函数响应式编程思想
Rxswift学习之(一)函数响应式编程思想 1. 函数响应式编程思想必备基本概念简介 2. iOS中三种编程思想:链式.函数式和响应式编程 2.1 链式编程 2.2 函数式编程 2.3 响应式编程 ...
最新文章
- 机器学习(14)逻辑回归(实战) -- 癌症分析
- 基于JDK1.8---HashMap源码分析
- Waiting for Debugger
- java知识积累——单元测试和JUnit(二)
- 【LeetCode从零单排】No27.Remove Element
- [转]全面了解setjmp与longjmp的使用
- 深度学习特征归一化方法——BN、LN、IN、GN
- python截取指定字符串_python 正则匹配获取指定多个词的在字符串(句子/段落)索引位置...
- 学习《让UpdatePanel支持文件上传》系列文章的相关链接
- 叉姐训练目录,好好搞搞,两个月要搞定哦
- 数据库的增删改查基本操作
- bzoj1984 月下“毛景树”
- “死神”百草枯:每年超万人中毒 没有解药
- 柯美服务器处理文件慢,处理打印机在打印文件时打印速度过慢的原因 看完你就知道了...
- 【通信】Matlab实现多同步压缩变换
- 服务器没有立即响应请求,服务器没有及时响应或控制请求
- KF、EKF、ESKF的区别与联系
- 新网站建设的完整步骤
- Linux学习:第一天_笔记
- 架构师进阶之路,JAVA架构师面试题
热门文章
- Hive分析函数--row_number 的用法
- (转)Redis上踩过的一些坑-美团
- 关于python学习,最系统的学习网站看这里
- Python浅谈gevent实现协程
- ExtJS的extend(Ext Designer的使用)
- Python《搞事情==蜂-鸟-图-片(二)》
- 深度学习《CGAN新认识》
- 《BERT:Pre-training of Deep Bidirectional Transformers for Language Understanding》
- Topic model相关文章总结
- org.json的使用详解