RxCocoa的一些基本概念

本文介绍了RxSwift的补充库RxCocoa,重点讲解如何利用RxCocoa进行响应式编程,包括响应式用户交互、响应式网络请求及数据模型与UI控件之间的绑定。文章深入探讨了ControlProperty和UIBindingObserver等核心概念,并解释了如何使用Units简化UI操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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能够变的非常强大和节约时间,




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值