RxSwift学习1-初体验感受Rx的魅力

371 阅读4分钟

[TOC]

RxSwift地址

RxSwift学习的必要性

复合 - Rx 就是复合的代名词
复用 - 复用性比较强 - 代码量降低
清晰 - 因为声明都是不可变更的,代码函数式编程可读性强,高内聚,低耦合
易用 - 理解容易,还抽象了异步编程,统一代码风格
稳定 - 因为 Rx 是完全通过单元测试的
装逼 - 代码的逼格很明显比原生高太多了,框架跨平台性,RxSwift思想处处可见

函数响应式编程(FRP)

RxSwift框架基于函数响应式编程思想进行封装,学习RxSwift也是对于编程思想的扩展
函数响应式编程主要包括 函数式 和 响应式 两部分

基本思想是函数,类似于这种
y=2x+b y=f(x) -> x=f(a) -> y = f(f(a))
例如,想从数组中获取:1. 大于3的数字,2.获取的数字之后 + 1,3.所有数字中的偶数

let array = [1, 2, 3, 4, 5, 6, 7]
//传统方式
for num in array {
    if num > 3 {
        let number = num + 1
        if (number % 2 == 0) {
            print(number)
        }
    }
}
//函数响应式编程方式
array.filter { $0 > 3 }
            .filter { ($0+1) % 2 == 0 }
            .forEach { print($0+1) }
            

函数式定义了变量之间的函数关系,具有 利用面向函数、可读性更高、思路更清晰、分段式更简洁、可分解等特点
响应式编程面向数据流和变化传播的编程范式
数据流:只能以事先规定好的顺序被读取一次的数据的一个序列
变化传播:类似观察者模式,发生变化就通知他人
两者一结合就是函数响应式编程

RxSwift代码初体验

代码准备

class ViewController: UIViewController {
    
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var textFiled: UITextField!
    @IBOutlet weak var button: UIButton!
    @IBOutlet weak var scrollView: UIScrollView!
    
    var person: Person = Person()
    let disposeBag = DisposeBag()
    var timer: Observable<Int>!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        }
    }
}

class Person: NSObject {
   @objc dynamic var name: String = "姓名"
}

KVO

常规方式的kvo :1.添加要观察的属性,2.处理属性变化的响应逻辑,3.移除观察者

func setupKVO() {
    //三部曲
    self.person.addObserver(self, forKeyPath: "name", options: .new, context: nil)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    textFiled.resignFirstResponder()
    print("来了")
    person.name = "\(person.name) 6"
    // print(person.name)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    print("响应")
    print(change as Any)
}

deinit {
    self.removeObserver(self.person, forKeyPath: "name", context: nil)
}

rx的方式,简洁明了:1.创建序列,2.订阅序列,3.回收序列

func setupKVO() {
    self.person.rx.observeWeakly(String.self, "name")
        .subscribe { value in
            print(value as Any)
        }
        .disposed(by: disposeBag)
}

Button

常规方式,addTarget - action

func setupButton() {
        //功能逻辑/业务逻辑分开了
        self.button.addTarget(self, action: #selector(<#T##@objc method#>), for: .touchUpInside)
}
@objc func didClickButton() {
        print("点了")
}   

rx方式,已经进行了封装

self.button.rx.controlEvent(.touchUpInside)
            .subscribe { _ in
                print("点击事件")
                
            }
            .disposed(by: disposeBag)
            
//或者
self.button.rx.tap
        .subscribe { _ in
            print("点击事件")
        
        }
        .disposed(by: disposeBag)

TextFiled

与Button使用方法相似,RxSwift更加面向开发者,这里可以做TextField和Button的关联,当输入内容之后,Button的title同时变化,非常的方便

func setupTextFiled() {
    self.textFiled.rx.text.orEmpty.changed
        .subscribe { text in
            print(text)
        }
        .disposed(by: disposeBag)
    //切忌盲目使用
    self.textFiled.rx.text
        .bind(to: self.button.rx.title())
        .disposed(by: disposeBag)
}

ScrollView

func setupScrollerView() {
    scrollView.rx.contentOffset
        .subscribe { [weak self]content in
            self?.view.backgroundColor = UIColor.init(red: content.y/255*0.8, green: content.y/255*0.6, blue: content.y/255*0.3, alpha: 1)
        }
        .disposed(by: disposeBag)
}

GestureRecognizer

func setupGestureRecognizer() {
    let tap = UITapGestureRecognizer()
    self.label.addGestureRecognizer(tap)
    self.label.isUserInteractionEnabled = true
    tap.rx.event.subscribe { tap in
        print(tap.view!)
    }
    .disposed(by: disposeBag)
}

Notification

func setupNotification() {
    NotificationCenter.default.rx.notification(UIResponder.keyboardWillShowNotification)
        .subscribe { noti in
            print(noti)
        }
        .disposed(by: disposeBag)
}

Timer

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

}

这里Timer不会影响scrollview滑动,因为Timer是rx自己定义的状态的改变

Network

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()

    //代码优势: 分层处理业务逻辑
    //task封装 外界不需要了解
    URLSession.shared.rx.response(request: URLRequest(url: url!))
        .subscribe { response, data in
            print(response)
        }
        .disposed(by: disposeBag)
}

初体验相关代码实现探索

既然万物皆rx,那么rx的实现过程探索一下

@dynamicMemberLookup
public struct Reactive<Base> {
    /// Base object to extend.
    public let base: Base

    /// Creates extensions with base object.
    ///
    /// - parameter base: Base object.
    public init(_ base: Base) {
        self.base = base
    }

    /// Automatically synthesized binder for a key path between the reactive
    /// base and one of its properties
    public subscript<Property>(dynamicMember keyPath: ReferenceWritableKeyPath<Base, Property>) -> Binder<Property> where Base: AnyObject {
        Binder(self.base) { base, value in
            base[keyPath: keyPath] = value
        }
    }
}

/// A type that has reactive extensions.
public protocol ReactiveCompatible {
    /// Extended type
    associatedtype ReactiveBase

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

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

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

/// Extend NSObject with `rx` proxy.
extension NSObject: ReactiveCompatible { }

由此我们看出rx是NSObject的扩展协议 ReactiveCompatible
public protocol ReactiveCompatible协议定义了 关联属性rx
NSObject遵循ReactiveCompatible协议
在ReactiveCompatible的扩展 extension ReactiveCompatible 中实现了 协议方法(关联属性方法)rx,就实现了每一个NSObject都有了rx 实现了 协议 - 扩展 - 遵循 的过程