前言:該篇主要來討論一下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)
}
}
複製代碼
爲何在輸入前會執行兩次?