Skip to content

Reactive

ShenYj edited this page Mar 30, 2022 · 1 revision

Reactive.swift

.

前言

在使用 RxSwift 时,我们经常会书写如下代码:

万物.rx.xxx

在使用过 SnapKitKingFisher 等优秀的开源库时,已感受到这种代码实现的魅力,但当时只是觉得这种命名空间的形式很新鲜,最明显的感受就是接口隔离,避免了 Object-C 那种臃肿的类前缀、方法前缀的写法(比如早期 AFN 与 SDWebImage 命名冲突),而再次在 RxSwift 遇到这种写法时,不再局限于设置布局、设置图片,而是适用于万物,感受到了 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 中的三大金刚: classstructenum 都是允许遵循协议使用的,苹果官方也是尽可能的建议我们使用 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

Getting Started

Social

Clone this wiki locally