RxSwift 源碼解析01:RxSwift初體驗

本文主要是RxSwift Demo的體驗

RxSwift 介紹

  • RxSwift = ReactiveX + Swift

    • ReactiveX(簡稱 Rx):是一個可以幫助我們簡化異步編程的框架
    • Swift:表示是Rx的swift版本
  • RxSwift 作為一個響應(yīng)式編程框架,有以下兩點好處:

    • 聚合邏輯:即通過RxSwift提供的各種操作符實現(xiàn)邏輯的聚合
    • 響應(yīng)式編程:即將所有事件都描述成一個可監(jiān)聽的序列,并提供大量功能各異的操作符,通過聲明式的語句來完成數(shù)據(jù)的獲取,轉(zhuǎn)換,結(jié)合及綁定

為什么要使用RxSwift?

  • 復(fù)合 - Rx 就是復(fù)合的代名詞
  • 復(fù)用 - 因為它易復(fù)合
  • 清晰 - 因為聲明都是不可變更的
  • 易用 - 因為它抽象了異步編程,使我們統(tǒng)一了代碼風(fēng)格
  • 穩(wěn)定 - 因為 Rx 是完全通過單元測試的
  • 高大上 - 代碼檔次比原生高很多

如果對于響應(yīng)式編程還不了解的同學(xué),可以先閱讀# iOS 底層原理37:鏈式編程 中對響應(yīng)式編程的介紹

初體驗

通過 Cocoapods 導(dǎo)入

    pod 'RxSwift', '6.5.0'
    pod 'RxCocoa', '6.5.0'

RxSwift 的使用步驟分為以下幾步:

  • 創(chuàng)建序列(萬物皆可 rx
  • 訂閱信號
  • 發(fā)送信號
  • 輸出結(jié)果

主要從以下幾方面來體驗

  • KVO
  • Target-Action:UIbutton
  • 代理:delegate
  • 手勢
  • 通知:Notification
  • timer
  • 網(wǎng)絡(luò)請求:URLSession
  • 多個任務(wù)間有依賴關(guān)系
  • 等待多個并發(fā)任務(wù)完成后處理結(jié)果

KVO

在Swift中實現(xiàn) OC 的KVO

  • 傳統(tǒng)實現(xiàn)

KVO 三部曲

  • 監(jiān)聽
  • 響應(yīng)
  • 銷毀
class Person: NSObject {
    //由于swift是靜態(tài)語言,而KVO是運行時發(fā)生的,所以需要加 dynamic 關(guān)鍵字
    @objc dynamic var name: String = "CJL"
}
//1-設(shè)置監(jiān)聽
func  setupKVO(){
    self.person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
}
//2-回調(diào)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    print("響應(yīng)")
    print(change as Any)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("來了")
    person.name = "\(person.name) 6"
    print(person.name)
}
//3-移除監(jiān)聽
deinit{
    self.removeObserver(self, forKeyPath: "name", context: nil)
}
  • Rx實現(xiàn)
func  setupKVO(){
    self.person.rx.observeWeakly(String.self, "name")
        .subscribe { value in
            print(value as Any)
        }
        .disposed(by: disposeBag)
    
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    print("來了")
    person.name = "\(person.name) 6"
}

Target-Action

這里以 UIButton 點擊事件為例

  • 傳統(tǒng)實現(xiàn)
var button: UIButton = {
    let btn = UIButton(type: .custom)
    btn.frame = CGRect(x: 100, y: 100, width: 200, height: 100)
    btn.setTitle("按鈕點擊", for: .normal)
    btn.backgroundColor = UIColor.lightGray
    return btn
}()

func setupButton(){
    button.addTarget(self, action: #selector(didClickButton), for: .touchUpInside)
}
@objc func didClickButton(){
    print("點擊按鈕")
}
  • Rx實現(xiàn)
func setupButton(){
    button.rx.controlEvent(.touchUpInside)
        .subscribe { _ in
            print("點擊事件")
        }
        .disposed(by: disposeBag)
}

代理

這里以 UITextFiledDelegate 代理方法為例

  • 傳統(tǒng)實現(xiàn)
func textFieldDidChangeSelection(_ textField: UITextField) {
    print(textField.text)
}
  • Rx實現(xiàn)
func setupTextField(){
    textFiled.rx.text.orEmpty.changed
        .subscribe { text in
            print(text)
        }
        .disposed(by: disposeBag)
}

此時還可以將 TextField 跟 button進行綁定,將textfiled輸入的文本作為 button的標題

func setupTextField(){
    //textFiled跟button綁定,輸入的內(nèi)容作為button的title
    textFiled.rx.text
        .bind(to: button.rx.title())
        .disposed(by: disposeBag)
}

手勢

這里以 UITapGestureRecognizer 點擊手勢為例

  • 傳統(tǒng)實現(xiàn)
func setupGestureRecongizer(){
    let tap = UITapGestureRecognizer()
    self.view.addGestureRecognizer(tap)
    self.view.isUserInteractionEnabled = true
    tap.addTarget(self, action: #selector(tapAction(_:)))
}
@objc func tapAction(_ tap: UITapGestureRecognizer){
    print(tap.view)
}
  • Rx實現(xiàn)
func setupGestureRecongizer(){
    let tap = UITapGestureRecognizer()
    self.view.addGestureRecognizer(tap)
    self.view.isUserInteractionEnabled = true

    tap.rx.event
        .subscribe { tap in
            print(tap.view)
        }
        .disposed(by: disposeBag)
    
}

通知

這里以 keyboardWillShowNotification(鍵盤彈起通知)為例

  • 傳統(tǒng)實現(xiàn)
func setupNotification(){
    // 監(jiān)聽鍵盤彈出通知
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name:UIResponder.keyboardWillShowNotification,object: nil)
}
@objc func keyboardWillShow(_ noti: Notification){
    print("鍵盤彈起")
}
  • Rx實現(xiàn)
func setupNotification(){
    // 監(jiān)聽鍵盤彈出通知
    NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
            .subscribe { noti in
                print("鍵盤彈起")
            }
            .disposed(by: disposeBag)
}

timer

實現(xiàn)計時器功能

  • 傳統(tǒng)實現(xiàn)

5s倒計時

func setupTimer(){
    var countDownNum = 5
    let countdownTimer = Timer(timeInterval: 1.0, repeats: true) { timer in
        if countDownNum == 0 {
              // 銷毀計時器
            timer.invalidate()
            // countDownNum = 5
            print(">>> Timer has Stopped!")
        } else {
            print(">>> Countdown Number: \(countDownNum)")
            countDownNum -= 1
        }
    }
    // 設(shè)置寬容度
    countdownTimer.tolerance = 0.2
    // 添加到當(dāng)前 RunLoop,mode為默認。
    RunLoop.current.add(countdownTimer, forMode: .default)
    // 開始計時
    countdownTimer.fire()
}
  • Rx實現(xiàn)

正向計時

注:RxSwift 中的 timer并不是我們常見的幾種timer定義的,而是自定義的,即不斷創(chuàng)建序列,發(fā)送信號來實現(xiàn)的定時器效果,這個后面篇章會詳細講解

func setupTimer(){
    var timer: Observable<Int> = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
        timer.subscribe { num in
            print(num)
        }
        .disposed(by: disposeBag)
}

網(wǎng)絡(luò)請求

  • 傳統(tǒng)實現(xiàn)
func setupNetwork() {
    let url = URL(string: "https://www.baidu.com")
    URLSession.shared.dataTask(with: url!) { data, response, error in
        print(String.init(data: data!, encoding: .utf8)!)
    }.resume()
}
  • Rx實現(xiàn)
func setupNetwork() {
    let url = URL(string: "https://www.baidu.com")
    URLSession.shared.rx.response(request: URLRequest(url: url))
        .subscribe { response, data in
            print(response)
        }
        .disposed(by: disposeBag)
}

多個任務(wù)間有依賴關(guān)系

通過用戶名密碼取得 Token 然后通過 Token 取得用戶信息,

  • 傳統(tǒng)實現(xiàn)
// 用回調(diào)的方式封裝接口
enum API {
    /// 通過用戶名密碼取得一個 token
    static func token(username: String, password: String,
        success: (String) -> Void,
        failure: (Error) -> Void) {  }

    /// 通過 token 取得用戶信息
    static func userinfo(token: String,
        success: (String) -> Void,
        failure: (Error) -> Void) { }
}
func setupTaskDependency(){
    /// 通過用戶名和密碼獲取用戶信息
    API.token(username: "111", password: "222") { token in
        API.userinfo(token: token) { userinfo in
            print("獲取用戶信息成功: \(userinfo)")
        } failure: { error in
            print("獲取用戶信息失敗: \(error)")
        }

    } failure: { error in
        print("獲取用戶信息失敗: \(error)")
    }
}
  • Rx實現(xiàn)

避免了回調(diào)地獄,使得代碼易讀、已維護

/// 用 Rx 封裝接口
enum API {
    /// 通過用戶名密碼取得一個 token
    static func token(username: String, password: String) -> Observable<String> {
        return Observable<String>.create{ observer in
            observer.onNext("1")
            return Disposables.create()
        }
    }

    /// 通過 token 取得用戶信息
    static func userInfo(token: String) -> Observable<String> {
        return Observable<String>.create{ observer in
            observer.onNext("2")
            return Disposables.create()
        }
    }
}
func setupTaskDependency(){
    API.token(username: "111", password: "222")
        .flatMapLatest(API.userInfo)
        .subscribe(onNext: { userInfo in
            print("獲取用戶信息成功: \(userInfo)")
        }, onError: { error in
            print("獲取用戶信息失敗: \(error)")
        })
        .disposed(by: disposeBag)
}

等待多個并發(fā)任務(wù)完成后處理結(jié)果

需要將兩個網(wǎng)絡(luò)請求合并成一個

  • 傳統(tǒng)實現(xiàn)
let queue1:DispatchQueue = DispatchQueue.init(label: "textQueue1")
let queue2:DispatchQueue = DispatchQueue.init(label: "textQueue2")
// 隊列queue加入隊列組
queue1.async (group: self.gropQueue){
    /*任務(wù)1*/
    print("獲取老師信息接口")

}
queue2.async (group: self.gropQueue){
    /*任務(wù)2*/
    print("獲取老師評論接口")
}
/*2個異步任務(wù)完成后執(zhí)行界面處理操作*/
self.gropQueue.notify(queue: DispatchQueue.main) {
    /*界面處理*/
    print("統(tǒng)一處理")
}
  • Rx實現(xiàn)

用幾行代碼實現(xiàn)復(fù)雜的異步操作

/// 用 Rx 封裝接口
enum API2 {
    /// 取得老師的詳細信息
    static func teacher(teacherId: Int) -> Observable<String> {
        return Observable<String>.create{ observer in
            observer.onNext("取得老師的詳細信息")
            return Disposables.create()
        }
    }

    /// 取得老師的評論
    static func teacherComments(teacherId: Int) -> Observable<[String]> {
        return Observable<[String]>.create{ observer in
            observer.onNext(["取得老師的評論"])
            return Disposables.create()
        }
    }
}
func setupConcurrentTasks(){
    /// 同時取得老師信息和老師評論
    Observable.zip(
          API2.teacher(teacherId: 110),
          API2.teacherComments(teacherId: 130)
        ).subscribe(onNext: { (teacher, comments) in
            print("獲取老師信息成功: \(teacher)")
            print("獲取老師評論成功: \(comments.count) 條")
        }, onError: { error in
            print("獲取老師信息或評論失敗: \(error)")
        })
        .disposed(by: disposeBag)
}

疑問點

從上面的實現(xiàn)看,主要關(guān)注以下方面:

  • 其他對象調(diào)用的 rx 到底是什么?為什么都可以調(diào)用 rx?
  • timer 的創(chuàng)建為什么跟其他對象有所不同?
  • Observable、Observer是什么?

其他對象調(diào)用的 rx 到底是什么?為什么都可以調(diào)用 rx?

  • 點擊 rx 進入其源碼實現(xiàn)
/// A type that has reactive extensions.
public protocol ReactiveCompatible {
    /// Extended type  關(guān)聯(lián)屬性
    associatedtype ReactiveBase

    /// Reactive extensions.
    static var rx: Reactive<ReactiveBase>.Type { get set }

    /// Reactive extensions.
    var rx: Reactive<ReactiveBase> { get set }
}

//協(xié)議的擴展
extension ReactiveCompatible {
    /// Reactive extensions.
    public static var rx: Reactive<Self>.Type {
        get { Reactive<Self>.self }
        // this enables using Reactive to "mutate" base type
        // swiftlint:disable:next unused_setter_value
        set { }
    }

    /// Reactive extensions.
    public var rx: Reactive<Self> {
        get { Reactive(self) }
        // this enables using Reactive to "mutate" base object
        // swiftlint:disable:next unused_setter_value
        set { }
    }
}

import Foundation

// NSObject 遵循了 rx 所在的協(xié)議
/// Extend NSObject with `rx` proxy.
extension NSObject: ReactiveCompatible { }

  • 從源碼可以看出 rx 是 ReactiveCompatible 協(xié)議的屬性,而NSObject 遵循了 ReactiveCompatible,而我們常說 萬物皆對象,對象溯源到底最終都指向 NSObject(即 rx 是 NSObject的擴展),所以這里得出一個結(jié)論:萬物即可 rx

其余問題我們將在下一節(jié)來進行一一探索

參考文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容