之前学了下RAC,在RAC当中的话其实基本都是block,它的中心思想其实包含了KVO和迭代器模式的思想下一步需要做什么还有就是函数式编程。
在RxSwift中有几个概念比较重要的就是Observer也就是观察者,而Observerable是可被观察者,这个是个事件源,我们的观察者需要去订阅这个事件源
才会去收到消息的通知。在RxSwift中会把每个事件源都去看作是一个序列(sequence)。
下面我们会去进行调用最多的就是Observable.方法其实就是去创建一个事件序列,然后我们去订阅它。
下面介绍下just方法其实就是去创建一个sequence,然后这个sequence中只会有一个事件,这个事件中包裹的内容也就是我们传进入的hello world,下面就会打印出hello world,而且会自动的去做完成操作
let disposeBag = DisposeBag()
let justSequence = Observable.just("hello world")
justSequence.subscribe(onNext: {
print($0)
}).disposed(by: DisposeBag)
因为我们在去使用这个订阅方法的时候,也就是说会先去发送next,再去发送completed
let disposeBag = DisposeBag()
let justSequence = Observable.just("hello world")
justSequence.subscribe { (event) in
print(event)
}
self.justSequence = justSequence
会输出的是 紧接着我们来看个东西,一个枚举值,这里其实就是说next就是序列中的新元素产生的,会去处理,error就是序列中的元素发生错误,处理错误,结束整个序列,对所有的观察者取消订阅,其实这里的元素可以理解为信号,complete标记整个序列已经完成,取消所有观察者的订阅,一个序列发送 completed 或者 error 事件时,不会再去产生新的信号了,
public enum Event<Element> {
/// Next element is produced.
case next(Element)
/// Sequence terminated with an error.
case error(Swift.Error)
/// Sequence completed successfully.
case completed
}
下面还需要说一下在Swift中我们不需要去写出enum的实际名称也就是case Event.next:print(value)。因为类型检查器能够自动为此进行类型推算,在下面其实我们就是通过一个集合去创建一个序列.
let fromSequence = Observable.from(["1","2","3","4"])
fromSequence.subscribe { (event) in
switch event{
case .next(let value):
print(value)
case .error(let error):
print(error)
case .completed:
print("完成")
}
}
打印如下
其中上面调用的是on:Event订阅所有的内容了就是说不管发生了错误也好还是完成了也好我都会去监听,去做处理
接下来我们还可以自己去创建一个自定义序列,这里把可被观察者给创建出来了,然后这个函数接收观察者作为其的参数
_ = Observable<String>.create({ observer in
observer.on(.next("哈哈"))
observer.on(.completed)
return Disposables.create()
}).subscribe{ x in
print(x)
}
我们可以看看create的,我们传入的observer应该就是在RxSwift.AnyObserver<Self.E>这里面做了实现,可以看到最后返回的是一个可被观察者Observable
public static func create(_ subscribe: @escaping (RxSwift.AnyObserver<Self.E>) -> Disposable) -> RxSwift.Observable<Self.E>
再简单的介绍下Subject,Subject即可以是Observable也可以是一个Observer,也就是说它是一个既可以去发送事件也可以去监听事件。
PublishSubject:订阅PublishSubject的时候,只能接收到订阅它之后发生的事件,前面发送的事件是不会接受的
let publishSubject = PublishSubject<Any>()
publishSubject.subscribe { (value) in
print(value)
}
//创建序列中一个事件,这个事件的内容是Hello
publishSubject.onNext("Hello")
publishSubject.onNext("world")
publishSubject.onNext("world")
ReplaySubject:订阅ReplaySubject的时候,可以接收到订阅它之后的事件,也可以接受订阅它之前发出的事件,接受几个事件取决与bufferSize的大小,下面就是只去接收前面两个事件
let replySubject = ReplaySubject<String>.create(bufferSize: 2)
replySubject.onNext("1")
replySubject.onNext("2")
replySubject.onNext("3")
replySubject.subscribe { (value) in
print("打印的值为\(value)")
}
replySubject.onNext("4")
replySubject.onNext("5")
BehaviorSubject:订阅了BehaviorSubject,默认情况下订阅者只能接受前面的最新的一条消息,而后面的都可以收到,这里的BehaviorSubject其实也就是为了防止第一个订阅者没有一个前一个信号可以接收而设立的
let behaviorSubject = BehaviorSubject(value: "A")
behaviorSubject.onNext("E")
behaviorSubject.onNext("F")
behaviorSubject.subscribe({
print($0)
})
behaviorSubject.onNext("B")
behaviorSubject.onNext("C")
behaviorSubject.subscribe({
print("2----\($0)")
})
behaviorSubject.onNext("D")
behaviorSubject.onNext("H")
再记录一个Variable
Variable可以解释成是一个可监听的数据结构。使用Variable,可以监听数据的变化,当Variable被释放的时候,它会完成通过asObservable
()所创建的序列,其实Variable里面是封装了BehaviorSubject的
Variable的使用,其实可以看出和BehaviorSubject一样都是只接收了上面的最新的一个信号,而后面的都是全部接收的,以及在通过asObservable()创建的序列释放的时候会去发送completed信号,因为当我们把variable设为全局属性的时候是没有去打印complete的
let variable = Variable(0)
variable.value=1;
variable.value=2;
let observable = variable.asObservable()
observable.subscribe(onNext: { (value) in
print("1、打印的值为\(value)")
}) {
print("complete")
}
variable.value=3;
variable.value=4;
variable.value=5;
observable.subscribe(onNext: { (value) in
print("2、打印的值为\(value)")
}) {
print("complete")
}
variable.value=6;
variable.value=7;
一般我们在开发的时候都会去创建一个全局的DisposeBag,就比如说我们可以把它引用为我们的属性,Disposebags 的作用跟ARC类似。当一个 Disposebags 被释放时,它会对内部每一个可以被清理的元素调用 dispose。DisposeBag 没有 dispose 方法,所以我们肯定不能去手动的调用。如果我们需要立即的清理资源,可以创建一个新的DisposeBag去替换掉我们之前强引用的那个Disposebags。用到这个主要想在事件完成之后去取消订阅,减少内存的使用和性能上面的消耗
一行代码可以绑定textField的text和label的text,这里的map其实就和RAC中的map是相似的,我不会立刻给你数据,我可以在里面先做完操作再给你。
textField.rx.text.map{
return "? \($0!)"
}
.bind(to:helloLabel.rx.text).disposed(by: disposeBag)
关于throttle和debounce这两个其实都是去做了限制事件的接受为什么这么说?就拿上面的举例子,如果我们不想让文本框一改变上面的label就紧跟着改变的话,其实我们就可以用到这两个中的其中一个,这两个都可以去限制在某个时间间隔内我不去接受你文本框改变所产生的那个事件。
再结合一个小案例,就是通过在文本框输入点击提交按钮之后可以把文本框里面的每次提交内容都插入label中。
//创建一个可监听的数组,其实这里用普通的可变数组也是可以的
let nameArray: Variable<[String]> = Variable([])
submitBtn.rx.tap.subscribe(onNext:{
if self.textField.text != ""
{
//把textField中的数据放入这个nameArray中
self.nameArray.value.append(self.textField.text!)
//将数组中的元素以,来分隔开来,调用下面的joined方法的元素必须是String
self.namesLabel.rx.text.onNext(self.nameArray.value.joined(separator: ","))
print(self.nameArray.value.joined(separator: ","))
//将文本框里面的内容清空
self.textField.rx.text.onNext("")
}
}).disposed(by: disposeBag)
首先看控制台打印也就是我们上面输出的数组经过插入
, 之后的内容
其中这里有一个注意点就是joined(separator: ",")这个方法,只能是数组里面的元素是String的时候才能使用,因为我们点进去看会看到下面的内容,这里面的Element==String就表示的是数组里面的元素的类型必须要是String
extension Array where Element == String {
/// Returns a new string by concatenating the elements of the sequence,
/// adding the given separator between each element.
///
/// The following example shows how an array of strings can be joined to a
/// single, comma-separated string:
///
/// let cast = ["Vivien", "Marlon", "Kim", "Karl"]
/// let list = cast.joined(separator: ", ")
/// print(list)
/// // Prints "Vivien, Marlon, Kim, Karl"
///
/// - Parameter separator: A string to insert between each of the elements
/// in this sequence. The default separator is an empty string.
/// - Returns: A single, concatenated string.
public func joined(separator: String = default) -> String
}
当然我们绑定textField的文本到label上面的文本,还有另外一种方式就是不经过map的这里为什么要
extension ControlPropertyType where Self.E == String? {
/// Transforms control property of type `String?` into control property of type `String`.
public var orEmpty: RxCocoa.ControlProperty<String> { get }
}
通过orEmpty?是因为其实就这里面将String?转为String处理,这样的话我们就不需要考虑String为nil的情况。在Rxswift中,对于所有字符串的监听都是转为orEmpty处理的
self.textField1.rx.text.orEmpty.bind(to:self.inputLabel.rx.text)
关于orEmpty其实框架里面也略微有解释
extension ControlPropertyType where Self.E == String? {
/// Transforms control property of type `String?` into control property of type `String`.
public var orEmpty: RxCocoa.ControlProperty<String> { get }
}
这里为什么可以绑定,其实我们用到的方法是bind(to: ObserverType)这个方法,其中ObserverType是一个协议,而我们的self.inputLabel.rx.text实际上是这个类型Binder<String?>
发在我们再点进去看,我们会发现它实现了ObserverType的协议,而实现ObserverType协议的是观察者对象,用于观察Observable发出的信号。所以我们这个label的就可以监听到textField信号的发出,然后去进行改变自己的值
还有一个那就是为什么这个textField是可被UILabel的rx.text观察的?而且它还可以被订阅。例如下面可以对它进行订阅
self.textField1.rx.text.subscribe(onNext: { (value) in
print("1、打印的值为\(value!)")
}) {
print("complete")
}
之所以可以去订阅它的原因其实是因为当我们去看UITextField+Rx的文件的时候会发现这个rx.text其实是一个ControlProperty<String?>类型的
之后我们再往ControlProperty<String?>点进去看会发现是个结构体,并且遵守了ControlPropertyType协议
再点进ControlPropertyType协议去看,发现其也继承了ObservableType协议和ObserverType协议,而上文提到的ControlProperty其实是一个实现ControlPropertyType的结构体,因此我们可以将ControlProperty的对象当作是一个Observable来使用,也就是一个可被观察者
介绍下never,这个never就是创建出的Sequence是不会发出任何信号的,我们有的时候可以把它和其他信号进行concat,这样也能起到屏蔽信号的作用
let neverSequence = Observable<Any>.never()
neverSequence.subscribe { (_) in
print("什么都不会发生")
}.disposed(by: disposeBag)
empty,创建一个空的sequence,只会去发送一个completed的事件,在下面我们可以看到打印的value都是completed,这里应用场景的话可能会用于用户做了某件事情你只需要告诉它结果,就比如说下单了,订单完成还是没完成。
let emptySequence = Observable<Any>.empty()
emptySequence.subscribe { (value) in
print(value)
}.disposed(by: disposeBag)
emptySequence.subscribe { (value) in
print(value)
}.disposed(by: disposeBag)
of就是创建出一个sequence,里面有很多事件信号,顺序发送出去,发送完成之后,然后再发送completed。这里of和from的区别其实就在于of是把它括号里面的1 2 3分别当做单独的事件,一个一个的拿出来发送,而from我们括号里面传入的是集合类,其实就是解析事件里面的元素,再把元素给一个个的去拿出来。
let ofSequence = Observable.of(1,2,3)
ofSequence.subscribe { (event) in
print(event)
}
error,创建了一个可观察的序列,只发出error事件并结束,其他事件不会发出
enum hhError:Error{
case test
}
Observable<Error>.error(hhError.test).subscribe({ (event) in
print(event)
})
输出为
这里再简单的介绍下Driver,Driver是驱动者,司机的意思,我们其实可以去理解为一个驱动者,在功能上它和被观察者Observable是类似的,它是可以和被观察者进行相互转换的,它会驱动着一个观察者,当它的序列中有事件的时候,被它驱动着的观察者就能进行相应的操作。一般我们会将一个Observable被观察者转换成Driver后再进行driver其实也就是开车操作做具体的事情,比如说绑定将某个值绑定到另一个值上面去。
它不会去直接的发送Error事件
它的观察订阅是发生在主线程(UI线程)的,比如说我们将一个UI控件的值绑定到另外一个UI控件,用drive是默认在主线程上执行的,不然的话我们调用绑定方法的时候如果再子线程上执行,那么在更新UI的时候是会出现问题的
本身就有使用shareReplayLatestWhileConnected,保证只被订阅一次,
Observable有个隐式的规定,就是在一个信号处理完成之前,不会发送下一个信号