RxCocoa的一些基本概念

来源:互联网 发布:甜甜圈软件 编辑:程序博客网 时间:2024/06/02 07:47
RxSwift是基础,它工作于各种类型的Swift,但是并不能指定用户交互、网络请求,但是RxCocoa就可以帮助我们做这些事情。RxCocoa是一个独立的库,允许我们使用许多预置的特性,这样能够更好的与UIKit和Cocoa进行整合。RxCocoa能够让我们进行响应式网络,响应式的用户交互和绑定数据模型到UI控件。大多数的UIKit控件都有响应式扩展,都是通过rx熟悉进行使用。比如:按钮的点击,我们经常会这样写:button.rx.tap,button.rx.tap返回的是ControlEvent,ControlEvent我们可以理解为是一种特殊类型的观察者序列(Observable),它可以使用.asObservable()方法转换为Observable。

RxCocoa能够工作在多平台,iOS (iPhone, iPad, Apple Watch), Apple TV ,macOS。每个平台都有一系列自定义的封装,提供了许多UI控件的扩展和一些SDK类。比如:UITextField+Rx.swift

extension Reactive where Base: UITextField {    /// Reactive wrapper for `text` property.    public var text: ControlProperty<String?> {        return value    }        /// Reactive wrapper for `text` property.    public var value: ControlProperty<String?> {        return UIControl.rx.value(            base,            getter: { textField in                textField.text            }, setter: { textField, value in                // This check is important because setting text value always clears control state                // including marked text selection which is imporant for proper input                 // when IME input method is used.                if textField.text != value {                    textField.text = value                }            }        )    } }
由上面源码可知,代码很少,主要是ControlProperty,那么ControlProperty是什么呢?:首先看一下相关的源码:

public struct ControlProperty<PropertyType> : ControlPropertyType {    public typealias E = PropertyType    ...省略部分源码    /// Subscribes an observer to control property values.    ///    /// - parameter observer: Observer to subscribe to property values.    /// - returns: Disposable object that can be used to unsubscribe the observer from receiving control property values.    public func subscribe<O : ObserverType>(_ observer: O) -> Disposable where O.E == E {        return _values.subscribe(observer)    }    ...省略部分源码    /// - returns: `Observable` interface.    public func asObservable() -> Observable<E> {        return _values    }    /// - returns: `ControlProperty` interface.    public func asControlProperty() -> ControlProperty<E> {        return self    }    /// Binds event to user interface.    ///    /// - In case next element is received, it is being set to control value.    /// - In case error is received, DEBUG buids raise fatal error, RELEASE builds log event to standard output.    /// - In case sequence completes, nothing happens.    public func on(_ event: Event<E>) {        switch event {        case .error(let error):            bindingErrorToInterface(error)        case .next:            _valueSink.on(event)        case .completed:            _valueSink.on(event)        }    }}
ControlProperty遵守了ControlPropertyType协议。在ControlProperty结构体中,有相应的订阅、转换为观察者序列、以及发送事件等一系列方法,由此我们可以猜测,ControlProperty既是观察者也是观察者序列。那么接下来看一下ControlPropertyType协议。

/// Protocol that enables extension of `ControlProperty`.public protocol ControlPropertyType : ObservableType, ObserverType {    /// - returns: `ControlProperty` interface    func asControlProperty() -> ControlProperty<E>}
可以看到,ControlPropertyType遵守了ObservableType, ObserverType,所以我们可以把ControlProperty理解为一种特殊类型的subject,能够被订阅而且能够有新值的注入。对于text属性直接与UITextField的内部属性text相关联。
再看一下UILabel+Rx.swift文件

extension Reactive where Base: UILabel {        /// Bindable sink for `text` property.    public var text: UIBindingObserver<Base, String?> {        return UIBindingObserver(UIElement: self.base) { label, text in            label.text = text        }    }    /// Bindable sink for `attributedText` property.    public var attributedText: UIBindingObserver<Base, NSAttributedString?> {        return UIBindingObserver(UIElement: self.base) { label, text in            label.attributedText = text        }    }    }
这里我们可以看到两个属性,text和attributedText,两个属性都与UILabel对应属性相关联,非常简单。最重要的是UIBindingObserver。那么UIBindingObserver又是什么呢?

public final class UIBindingObserver<UIElementType, Value> : ObserverType where UIElementType: AnyObject {    public typealias E = Value    weak var UIElement: UIElementType?    let binding: (UIElementType, Value) -> Void    /// Initializes `ViewBindingObserver` using    public init(UIElement: UIElementType, binding: @escaping (UIElementType, Value) -> Void) {        self.UIElement = UIElement        self.binding = binding    }    /// Binds next element to owner view as described in `binding`.    public func on(_ event: Event<Value>) {        if !DispatchQueue.isMain {            DispatchQueue.main.async {                self.on(event)            }            return        }        switch event {        case .next(let element):            if let view = self.UIElement {                binding(view, element)            }        case .error(let error):            bindingErrorToInterface(error)        case .completed:            break        }    }    /// Erases type of observer.    ///    /// - returns: type erased observer.    public func asObserver() -> AnyObserver<Value> {        return AnyObserver(eventHandler: on)    }}

由上面源码可知,UIBindingObserver类似于ControlProperty,也是一种类型的观察者,主要是用于UI控件。UIBindingObserver用于绑定UI和潜在的逻辑,但是它并不能绑定错误事件。如果错误事件发送给UIBindingObserver,这会调用fatalError(),打印相应的错误信息。对于UIBindingObserver还需要注意两点:

1: 不能够绑定错误事件

2: 必须在主线程执行绑定操作


绑定观察者序列(Binding observables)
     
在RxCocoa中绑定是单向的,如下图:只能由生产者到接受者,不能反过来操作。生产者产生值,接受者接受值,接受者不能返回值,这是基本规则。


执行绑定操作的基础就是函数bindTo(_:)。为了绑定一个观察者序列(observable)到其它实体(实体就是subject,能够处理值也可以写值。subject是非常的重要,在Cocoa中。因为像UILabel, UITextField, and UIImageView都是可变数据,能够被设置值和获取值),接受者(receiver)必须遵守观察者协议(ObserverType)。但是bindTo(_:)不仅仅是用于绑定用户界面和潜在的数据,也可以用于其它目的,例如:你能够使用bindTo(_:)创建一个独立的过程(processes),以致于观察者序列(observable)能够触发一个subject并且执行一些后台任务而并不需要显示任何内容在屏幕上。
总而言之,bindTo(_:)就是特殊的 subscribe(_:)版本。
注意:当绑定UI控件时,RxCocoa将确认观察者序列是否被执行在主线程,如果不是,将调用fatalError()而且应用将奔溃,报错误:fatal error: Element can be bound to user interface only on MainThread.

Units
RxCocoa提供了更多高级的特性来工作于Cocoa和UIKit。超越bindTo,它提供了对于观察者序列(observables)的特殊实现。专门用于UI操作,那就是Units.Units是一群类,专业化的观察者序列,允许更加容易的写简单代码处理UI.Units官方描述:
Units also help communicate and ensure observable sequence properties across interface boundaries。
看起来有点抽象,那么考虑一些普通绑定观察者序列到用户界面控件(user interface controls)情况。为了能够更新UI,你需要经常在主线程订阅,而且你经常需要共享订阅者来绑定到多个UI组件,而且你并不想在UI的处理过程中出现错误。

由于以上的情况,所以我们来看一下Units的特性:
1: Units不能有错误出现 (Units can’t error out)
2: Units是在主线程进行监听 (Units are observed on main scheduler)
3: Units在主线程进行订阅(Units subscribe on main scheduler)
4: Units共享副作用(Units share side effects)

这些实体的存在确保了用户界面经常显示内容,而且被显示的数据经常以正确的方式处理,为了UI能够进行处理。在Units framework中有两个主要的组成部分部分:

1:ControlProperty and ControlEvent
ControlProperty遵守了ObservableType和ObserverType 协议,所以我们可以对其进行订阅,也可以发送新的值,用于专门的rx扩展,绑定数据到正确的用户界面控件。

ControlEvent是用于监听一系列的UI控件的事件,它是一种观察者序列,因为遵守了ObservableType比如:点击键盘的return按钮。如果控件使用了UIControlEvents保持跟踪控件的当前状态,ControlEvent是可以获取的.

2:Driver
Driver是一种特殊的观察者序列,也有着跟之前相同的约束,所以它不能发送错误事件。所有的处理过程都会被确保在主线程执行,避免了在后台线程改变UI。总而言之:Units是框架中的一种可选方案,你并不一定要使用它。如果你想一些好的编译简查和敏感的UI限制,Units能够变的非常强大和节约时间,




原创粉丝点击