Reactive - ShenYj/ShenYj.github.io GitHub Wiki
在使用 RxSwift
时,我们经常会书写如下代码:
万物.rx.xxx
在使用过 SnapKit
、KingFisher
等优秀的开源库时,已感受到这种代码实现的魅力,但当时只是觉得这种命名空间的形式很新鲜,最明显的感受就是接口隔离,避免了 Object-C 那种臃肿的类前缀、方法前缀的写法(比如早期 AFN 与 SDWebImage 命名冲突),而再次在 RxSwift
遇到这种写法时,不再局限于设置布局、设置图片,而是适用于万物,感受到了 RxSwift
强大的同时,被这种代码实现方式所吸引,是时候深刻学习研究一下了
-
Reactive.swift
完整源码 (RxSwift 6.5
)/** Use `Reactive` proxy as customization point for constrained protocol extensions. General pattern would be: // 1. Extend Reactive protocol with constrain on Base // Read as: Reactive Extension where Base is a SomeType extension Reactive where Base: SomeType { // 2. Put any specific reactive extension for SomeType here } With this approach we can have more specialized methods and properties using `Base` and not just specialized on common base type. `Binder`s are also automatically synthesized using `@dynamicMemberLookup` for writable reference properties of the reactive base. */ @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 { }
在 Reactive.swift
中可以分为三部分
-
struct Reactive
- 结构体
Reactive
包含一个成员base
, 也就是我们包装的实例对象,其类型通过泛型管理 -
subscript
方法是配合@dynamicMemberLookup
所实现的
- 结构体
-
protocol ReactiveCompatible
- 协议
ReactiveCompatible
为遵循者提供了Reactive
结构体类型rx
属性: 一个静态的,一个实例的,使用到了关联类型 - 通过
extension
给出rx
属性默认实现- 实例:直接实例化一个
Reactive<实例对象类型>
结构体实例,而结构内包装的base
成员就是遵循ReactiveCompatible
协议的实例对象 - 静态:不需要实例化,直接就是一个类型包装
- 实例:直接实例化一个
- 协议
-
extension NSObject
- 给
NSObject
默认遵循了ReactiveCompatible
,提供了rx
的扩展, 这样能默认覆盖绝大多数类
- 给
对比 RxSwift 5.0
最大的改变就是支持了动态成员查找 @dynamicMemberLookup
在查阅资料时,看到了这个关闭状态的 issuse
: ReactiveCompatible is a class protocol in RxSwift 6
Reactive.swift (RxSwift 5.0)
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
}
}
/// A type that has reactive extensions.
public protocol ReactiveCompatible {
/// Extended type
associatedtype ReactiveBase
@available(*, deprecated, renamed: "ReactiveBase")
typealias CompatibleType = 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 {
return Reactive<Self>.self
}
// swiftlint:disable:next unused_setter_value
set {
// this enables using Reactive to "mutate" base type
}
}
/// Reactive extensions.
public var rx: Reactive<Self> {
get {
return Reactive(self)
}
// swiftlint:disable:next unused_setter_value
set {
// this enables using Reactive to "mutate" base object
}
}
}
import class Foundation.NSObject
/// Extend NSObject with `rx` proxy.
extension NSObject: ReactiveCompatible { }
结合我自身的情况
第一次使用 RxSwift
时,使用的就是 5.0
这个版本,在第二个项目中升级到了 6.2
, 但是我似乎并没有感觉到什么异样
看标题的确是挺吓人,Swift
中的三大金刚: class
、struct
、enum
都是允许遵循协议使用的,苹果官方也是尽可能的建议我们使用 struct
替代 class
,如果像标题描述的那样,那的确是个不小的事
这条 issuse
讨论的信息不多,很快就可以读完,得到的信息是:
在
RxSwift 6.0
上,的确带来了这个副作用 (为了增添动态查找属性的能力,而限定了协议的使用范围),但很快就在RxSwift 6.1
版本修复了这个问题
Reactive now supports structs and value-types again, with the dynamic look-up specifically dealing with AnyObjects #2285