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)
              }
      }
复制代码

思考

为何在输入前会执行两次?

思考
相关文章
相关标签/搜索