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 { }
}
}
rx
是protocol ReactiveCompatible
的属性,static var rx
是类变量,var rx
是实例变量,变量类型为Reactive<ReactiveBase>
。associatedtype
关键字是为protocol
提供泛型功能。Reactive
遵循了ReactiveCompatible
协议,那么Reactive
的泛型就会替代ReactiveBase
。- 在Swift中,我们可以通过
extension protocol
来为协议的属性或者方法提供默认实现。
public struct Reactive<Base: AnyObject> {
/// base存储泛型变量
public let base: Base
/// 构造方法
public init(_ base: Base) {
self.base = base
}
}
Reactive
结构体有一个base
属性,存储了当前的实例对象,方便调用Base
的实例方法。
核心逻辑
-
定义一个协议
ReactiveCompatible
,协议拥有Reactive
类型的属性rx
。 -
NSObject
遵循协议ReactiveCompatible
,这样所有的类就都拥有了属性rx
。// NSObject遵循ReactiveCompatible协议,保证所有类都有rx属性 extension NSObject: ReactiveCompatible { }
-
我们通常通过以下方式来使用
RxSwift
。当执行button.rx
后,我们可以看到返回的类型为Reactive<UIButton>
,所以我们要添加tap
属性,扩展的应该是Reactive
类。
// RxSwift具体用法
let button = UIButton()
button.rx.tap
.subscribe(onNext: {
print("UIButton Tapped")
})
.disposed(by: disposeBag)
- 扩展
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)
}
}
}