前言:该篇主要来讨论一下RxSwift里面的定时器,UITextField.rx.text ,UITextView.rx.text的底层实现。swift
在探索RxSwift里面的定时器的实现前,咱们先来了解一下传统的实现定时器的方式api
1.使用NSTimerbash
func testNSTimer() {
_ = Timer.scheduledTimer(withTimeInterval: 1.0, repeats:true) { (time) in
print(time);
}
}
复制代码
2.使用CADisplayLinkasync
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.使用GCDoop
// 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())
}
复制代码
使用1,2咱们发现:UI滑动操做会阻塞定时器的执行,使用GCD的方式却不受UI滑动操做的影响,因此咱们大胆猜想RxSwift里面的定时器是封装了GCD。ui
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核心逻辑很重要)spa
一步步解析里面的代码,咱们会发现RxSwift的定时器是对GCD的封装3d
** 就是如下实现timer的核心代码,咱们会发现就是对GCD的封装**code
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 的实现:cdn
// 在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最终的封装是:
// 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)
}
}
复制代码
为何在输入前会执行两次?