RxSwift進階(四)-定時器、UITextField以及UITextView

前言:該篇主要來討論一下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())
   }
複製代碼

二.RxSwift定時器的實現

使用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的原理

一步步進入解析源碼,咱們發現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的原理

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)
              }
      }
複製代碼

思考

爲何在輸入前會執行兩次?

思考
相關文章
相關標籤/搜索