RxSwift-如何用协议实现一个命名空间

1,908 阅读2分钟

首发地址: www.ljcoder.com/16073927284…

RxSwift简介

RxSwift是一个函数响应式框架,使用到的是FRP(函数响应式编程)编程思想。它是通过构建函数操作数据序列,然后对这些序列做出响应的编程方式。基本的使用可以参考官方的RxSwift中文文档

rx是什么?

在RxSwift的使用过程中我们经常可以看到以下代码:

cameraButton.rx.tap
    .bind(to: imageView.rx.image)
    .disposed(by: disposeBag)

其中rx具体是什么呢?我们点击进去,找到以下源码

/// A type that has reactive extensions.
public protocol ReactiveCompatible: AnyObject {
    /// associatedtype,用来实现protocol的泛型功能
    associatedtype ReactiveBase: AnyObject

    /// Reactive的类变量
    static var rx: Reactive<ReactiveBase>.Type { get set }

    /// Reactive的实例变量
    var rx: Reactive<ReactiveBase> { get set }
}

// 通过extension protocol对两个rx变量提供默认get和set实现方法
extension ReactiveCompatible {
    /// 类变量的set和get默认实现
    public static var rx: Reactive<Self>.Type {
        get { Reactive<Self>.self }
        
        set { }
    }

    /// 实例变量的set和get默认实现
    public var rx: Reactive<Self> {
        get { Reactive(self) }

        set { }
    }
}

  1. rxprotocol ReactiveCompatible的属性,static var rx是类变量,var rx是实例变量,变量类型为Reactive<ReactiveBase>
  2. associatedtype关键字是为protocol提供泛型功能。Reactive遵循了ReactiveCompatible协议,那么Reactive的泛型就会替代ReactiveBase
  3. 在Swift中,我们可以通过extension protocol来为协议的属性或者方法提供默认实现。
public struct Reactive<Base: AnyObject> {
    /// base存储泛型变量
    public let base: Base

    /// 构造方法
    public init(_ base: Base) {
        self.base = base
    }
}

Reactive结构体有一个base属性,存储了当前的实例对象,方便调用Base的实例方法。

核心逻辑

  1. 定义一个协议ReactiveCompatible,协议拥有Reactive类型的属性rx

  2. NSObject遵循协议ReactiveCompatible,这样所有的类就都拥有了属性rx

    // NSObject遵循ReactiveCompatible协议,保证所有类都有rx属性
    extension NSObject: ReactiveCompatible { }
    
  3. 我们通常通过以下方式来使用RxSwift。当执行button.rx后,我们可以看到返回的类型为Reactive<UIButton>,所以我们要添加tap属性,扩展的应该是Reactive类。

// RxSwift具体用法
let button = UIButton()
button.rx.tap
        .subscribe(onNext: {
            print("UIButton Tapped")
        })
        .disposed(by: disposeBag)

  1. 扩展Reactive,添加tap属性,where Base: UIButton表示此属性只是给UIButton添加。
// 如果要对UIButton新增属性或者方法,是对Reactive结构体进行扩展,where语法限定扩展的属性或者方法只属于UIButton
extension Reactive where Base: UIButton {
    
        public var tap: ControlEvent<Void> {
            controlEvent(.touchUpInside)
        }
}

补充: Reactive结构体base属性的作用

URLSession扩展方法中,我们可以看到base属性的作用,主要是在调用URLSession自身的方法时用到。

extension Reactive where Base: URLSession {
    public func response(request: URLRequest) -> Observable<(response: HTTPURLResponse, data: Data)> {
        return Observable.create { observer in
            let d: Date?

            if URLSession.rx.shouldLogRequest(request) {
                d = Date()
            }
            else {
               d = nil
            }
            
            // 此时的base为URLSession的实例
            let task = self.base.dataTask(with: request) { data, response, error in

                if URLSession.rx.shouldLogRequest(request) {
                    let interval = Date().timeIntervalSince(d ?? Date())
                    print(convertURLRequestToCurlCommand(request))
                    #if os(Linux)
                        print(convertResponseToString(response, error.flatMap { $0 as NSError }, interval))
                    #else
                        print(convertResponseToString(response, error.map { $0 as NSError }, interval))
                    #endif
                }
                
                guard let response = response, let data = data else {
                    observer.on(.error(error ?? RxCocoaURLError.unknown))
                    return
                }

                guard let httpResponse = response as? HTTPURLResponse else {
                    observer.on(.error(RxCocoaURLError.nonHTTPResponse(response: response)))
                    return
                }

                observer.on(.next((httpResponse, data)))
                observer.on(.completed)
            }

            task.resume()

            return Disposables.create(with: task.cancel)
        }
    }
}