[TOC]
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
实现了 协议 - 扩展 - 遵循 的过程