前言:该篇主要来讨论一下RxSwift里面的定时器,UITextField.rx.text ,UITextView.rx.text的底层实现。
一.实现定时器的三种方式。
在探索RxSwift里面的定时器的实现前,我们先来了解一下传统的实现定时器的方式
1.使用NSTimer
func testNSTimer() {
_ = Timer.scheduledTimer(withTimeInterval: 1.0, repeats:true) { (time) in
print(time);
}
}
2.使用CADisplayLink
func testLinkTimer(){
linkTimer = CADisplayLink.init(target: self, selector: #selector(timerFire))
linkTimer?.preferredFramesPerSecond = 1;
linkTimer?.add(to: RunLoop.current, forMode: .default)
// linkTimer?.isPaused = true //暂停控制器
}
@objc func timerFire() {
print("linkTimer")
}
3.使用GCD
// GCD
func testGCDTimer(){
gcdTimer = DispatchSource.makeTimerSource();
gcdTimer?.schedule(deadline: DispatchTime.now(), repeating: DispatchTimeInterval.seconds(1))
gcdTimer?.setEventHandler(handler: {
print("gcd Timer")
})
gcdTimer?.resume()
// gcdTimer?.suspend() //暂停
// gcdTimer?.cancel(); //取消
// gcdTimer = nil // 销毁 (执行前,先执行cancel())
}
二.RxSwift定时器的实现
使用1,2我们发现:UI滑动操作会阻塞定时器的执行,使用GCD的方式却不受UI滑动操作的影响,所以我们大胆猜测RxSwift里面的定时器是封装了GCD。
var timer: Observable<Int>!
func setupTimer(){ //实现原理?
timer = Observable<Int>.interval(1, scheduler: MainScheduler.init())
timer.subscribe(onNext: { (num) in
print("定时器:\(num)")
}).disposed(by: disposeBag)
}
下面我们就一起来翻翻里面实现(理解RxSwift核心逻辑很重要)
一步步解析里面的代码,我们会发现RxSwift的定时器是对GCD的封装
** 就是以下实现timer的核心代码,我们会发现就是对GCD的封装**
func schedulePeriodic<StateType>(_ state: StateType, startAfter: RxTimeInterval, period: RxTimeInterval, action: @escaping (StateType) -> StateType) -> Disposable {
let initial = DispatchTime.now() + startAfter
var timerState = state
let timer = DispatchSource.makeTimerSource(queue: self.queue)
timer.schedule(deadline: initial, repeating: period, leeway: self.leeway)
...
timer.setEventHandler(handler: {
if cancelTimer.isDisposed {
return
}
timerState = action(timerState)
})
timer.resume()
return cancelTimer
}
三.textField.rx.text的原理
一步步进入解析源码,我们发现textField.rx.text 的实现:
// 在UIControl+Rx.swift里面
internal func controlPropertyWithDefaultEvents<T>(
editingEvents: UIControl.Event = [.allEditingEvents, .valueChanged],
getter: @escaping (Base) -> T,
setter: @escaping (Base, T) -> Void
) -> ControlProperty<T> {
return controlProperty(
editingEvents: editingEvents,
getter: getter,
setter: setter
)
}
四.textView.rx.text的原理
textView.rx.text最终的封装是:
// UITextView+Rx.swift
public var value: ControlProperty<String?> {
let source: Observable<String?> = Observable.deferred { [weak textView = self.base] in
let text = textView?.text
let textChanged = textView?.textStorage
.rx.didProcessEditingRangeChangeInLength
.observeOn(MainScheduler.asyncInstance)
.map { _ in
return textView?.textStorage.string
}
?? Observable.empty()
return textChanged
.startWith(text)
}
再看didProcessEditingRangeChangeInLength的实现,其实是对textView?.textStorage值的监听
//NSTextStorage+Rx.swift
public var didProcessEditingRangeChangeInLength: Observable<(editedMask: NSTextStorage.EditActions, editedRange: NSRange, delta: Int)> {
return delegate
.methodInvoked(#selector(NSTextStorageDelegate.textStorage(_:didProcessEditing:range:changeInLength:)))
.map { a in
let editedMask = NSTextStorage.EditActions(rawValue: try castOrThrow(UInt.self, a[1]) )
let editedRange = try castOrThrow(NSValue.self, a[2]).rangeValue
let delta = try castOrThrow(Int.self, a[3])
return (editedMask, editedRange, delta)
}
}
思考
为什么在输入前会执行两次?