RxSwift使用教程

来源:互联网 发布:swift完整项目源码 编辑:程序博客网 时间:2024/05/21 04:20

为了自己更好的学习而转发:

转发自:http://blog.csdn.net/Hello_Hwc/article/details/51859330


RxSwift是Swift函数响应式编程的一个开源库,由Github的ReactiveX组织开发,维护。

RxSwift的目的是让让数据/事件流和异步任务能够更方便的序列化处理,能够使用Swift进行响应式编程

目前,RxSwift在Github上收到了5000+Star,600+fork。

本文的目的

    介绍RxSwift的核心思想
    讲解RxSwift的基础使用
    介绍RxSwift的优点

如果你有时间,建议先读读RxSwift的文档,这会给你一个最基本的认识

本文不会讲解函数式编程,也不会讲解函数响应式编程的概念,计划后面单独出一篇博客来讲解Swift与函数式编程。

本文来自于官方文档的翻译,官方example代码的阅读,以及自己的理解
RxSwift和ReativeCocoa

老一点的iOS开发者应该对ReativeCocoa有一些了解,iOS响应式编程的鼻祖。就个人来看

    ReativeCocoa更适合OC,缺点语法复杂,概念繁多,参考资料少(尤其RAC4),不易理解
    RxSwift对Swift的兼容很好,利用了很多的Swift特性,语法简单,概念清楚

So,个人是非常推荐RxSwift的
Observables/Sequences

先复习下SequenceType。这是Swift中的一个协议,比如Swift中的Array就遵循这个协议,通过这个协议,你可以这样的去操作一个Array

let array = [1,2,3,4,5]
let array2 = array.filter({$0 > 1}).map({$0 * 2})//4 6 8 10
var indexGenerator = array2.generate()
let fisrt = indexGenerator.next() // 4
let seoncd = indexGenerator.next() //6

    1
    2
    3
    4
    5

    1
    2
    3
    4
    5

也就是说,把Array作为一个序列,然后依次对这个序列进行过滤,映射等操作,也可以通过indexGenerator来一个个的获取序列中的数据。

RxSwift的核心思想和这个类似。

RxSwift的核心是想是 Observable<Element> sequence,Observable表示可监听或者可观察,也就是说RxSwift的核心思想是可监听的序列。并且,Observable sequence可以接受异步信号,也就是说,信号是可以异步给监听者的

    Observable(ObservableType) 和 SequenceType类似
    ObservableType.subscribe 和 SequenceType.generate类似
    由于RxSwift支持异步获得信号,所以用ObservableType.subscribe,这和indexGenerator.next()类似

本文把RxSwift中的序列的每一个Element成为信号,因为异步的Element是与时间相关的,称作信号更好理解一点

RxSwift中,ObservableType.subscribe的回调(新的信号到来)一共有三种

enum Event<Element>  {
    case Next(Element)      // 新的信号到来
    case Error(ErrorType)   // 信号发生错误,序列不会再产生信号
    case Completed          // 序列发送信号完成,不会再产生新的信号
}
protocol ObserverType {
    func on(event: Event<Element>) //监听所有的信号
}

    1
    2
    3
    4
    5
    6
    7
    8
    9

    1
    2
    3
    4
    5
    6
    7
    8
    9

取消监听

Observable分为两种

    在有限的时间内会自动结束(Completed/Error),比如一个网络请求当作一个序列,当网络请求完成的时候,Observable自动结束,资源会被释放
    信号不会自己结束,最简单的比如一个Timer,每隔一段时间发送一个新的信号过来,这时候需要手动取消监听,来释放相应的资源,又比如一个label.rac_text是一个Obserable,通常需要这样调用addDisposableTo(disposeBag)来让其在deinit,也就是所有者要释放的时候,自动取消监听。

class Observable<Element> {
    func subscribe(observer: Observer<Element>) -> Disposable //调用Disposable的方法来取消
}

    1
    2
    3
    4

    1
    2
    3
    4

当然,除了手动释放,RxSwift提供了一些操作符,比如 takeUntil来根据条件取消

sequence
    .takeUntil(self.rx_deallocated) //当对象要释放的时候,取消监听
    .subscribe {
        print($0)
    }

    1
    2
    3
    4
    5

    1
    2
    3
    4
    5

信号处理的顺序

Observable有个隐式的约定,那就是在一个信号处理完成之前,不会发送下一个信号,不管发送信号的线程是并发的or串行的。

比如

someObservable
  .subscribe { (e: Event<Element>) in
      print("Event processing started")
      // processing
      print("Event processing ended")
  }

    1
    2
    3
    4
    5
    6

    1
    2
    3
    4
    5
    6

只会出现

Event processing started
Event processing ended
Event processing started
Event processing ended
Event processing started
Event processing ended

    1
    2
    3
    4
    5
    6

    1
    2
    3
    4
    5
    6

不会出现

Event processing started
Event processing started
Event processing ended
Event processing ended

    1
    2
    3
    4

    1
    2
    3
    4

第一个例子

我们监听textfield的文字变化,然后,Log出text,当button点击的时候,取消这次监听

class ObservableAndCancelController : UIViewController{
    var subscription:Disposable?

    @IBOutlet weak var textfield: UITextField!
    @IBAction func cancelObserve(sender: AnyObject) {
        subscription?.dispose()
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        subscription = textfield.rx_text.subscribeNext { (text) in
            print(text)
        }
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

RxSwift用extensiton的方式,为UITextfield,UIlabel等控件添加了很多可监听的属性,这里的textfield.rx_text就是一个

效果:随着文字输入,实时Log出textfield的文字,当点击button之后,再输入,则不会Log
操作符(Operators)

在上文的第一个例子里面,你看到了监听信号,并且log出值。事实上,这样直接处理信号的时候是很少的,很多时候,我们需要对信号进行映射,过滤,这时候我们就要用到操作符了。在这个文档里,你可以找到所有的操作符。

关于操作符效果,你可以参见http://rxmarbles.com/的可视化效果,这会给你一个更好的理解

例子二,map,filter,combineLatest

    map 对信号(Element)进行映射处理。比如输入是String,影射到Bool
    filter 对信号(Element)进行过滤处理。返回信号,和输入的信号是同一种类型
    combineLatest 对两种信号的值进行结合。可以返回不同种类的信号。

例如

     let firstObserverable = firstTextfield.rx_text.map({"first" + $0})
     let secondObserverable = secondTextfield.rx_text.filter({$0.characters.count > 3})
      _ =  Observable.combineLatest(firstObserverable, secondObserverable, resultSelector:{ ($0 + $1,$0.characters.count + $1.characters.count)}).subscribeNext { (element) in
            print("combineLatest:\(element)")
      }        

    1
    2
    3
    4
    5

    1
    2
    3
    4
    5

对于,每一个fistTextfield的信号,在字符串开始处增加”first”;对secondTextfield的信号进行过滤,当长度大于3的时候,才会继续传递。对两个信号进行结合,取truple类型,然后打印出来。

所以,当我在fistTextfield中,输入1234,然后secondTextfield中依次输入abcdefg的时候

combineLatest:("first1234abcd", 13)
combineLatest:("first1234abcd3", 14)
combineLatest:("first1234abcd", 13)
combineLatest:("first1234abcde", 14)
combineLatest:("first1234abcdef", 15)
combineLatest:("first1234abcdefg", 16)

    1
    2
    3
    4
    5
    6

    1
    2
    3
    4
    5
    6

例子三,创建一个Observable

Observerable可以用来处理任务,并且异步返回Event信号(Next,Error,Completion)

比如,这样一个方法

//Observable就是处理输入,并且把description发送出去
func createObserveable(object:AnyObject?)->Observable<String?>{
    return Observable.create({ observer in
        observer.onNext(object?.description)
        observer.onCompleted()
        return NopDisposable.instance
    })
}

    1
    2
    3
    4
    5
    6
    7
    8

    1
    2
    3
    4
    5
    6
    7
    8

这样调用

_ = createObserveable(test).subscribe({ (event) in
      switch event{
      case .Next(let value):
          print(value)
      case .Completed:
          print("Completed")
      case .Error(let error):
          print(error)
      }
  })

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10

然后,Log如下

Optional("{\n    a = b;\n    1 = 2;\n}")
Completed

    1
    2

    1
    2

可以看到,创建一个Observable相当容易,调用Observable.create,在必要的时候发送onNext,onError,onCompleted信号。然后返回一个Disposable用来取消信号
throttle/retry/distinctUntilChanged/flatMapLatest

    throttle 忽略上一个信号的一段时间的变化,也就是说一段时间没有新的信号输入,才会向下发送
    distinctUntilChanged 直到信号改变了再发送
    retry 如果失败,重新尝试的次数
    flatMapLatest 仅仅执行最新的信号,当有新的信号来的时候,取消上一次未执行完的整个序列

最直接的例子就是搜索,通常我们想要

    用户用一段时间没有输入的时候,在进进行网络请求,不然网络请求太频繁,对客户端和服务器都是负担
    当新的请求来的时候,如果上一个未完成,则取消上一个
    如果网络失败,能重新请求几次就更好了

这时候,用RxSwift你得代码会变的非常简单

let searchResults = searchBar.rx_text
    .throttle(0.3, scheduler: MainScheduler.instance)
    .distinctUntilChanged()
    .flatMapLatest { query -> Observable<[Repository]> in
        if query.isEmpty {
            return Observable.just([])
        }

        return doSearchAPI(query).retry(3)
            .catchErrorJustReturn([])
    }
    .observeOn(MainScheduler.instance)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

这里简单讲解下作用

    throttle(0.3, scheduler: MainScheduler.instance) 保证用户没有输入0.3秒后再进行下一步
    distinctUntilChanged() 假如0.3秒之前输入是ab,0.3秒后还是ab,则不会进行下一步,只有改变了才会进行下一步
    flatMapLatest 保证只搜索最新的,如果之前的没有完成,会被自动取消
    doSearchAPI(query).retry(3) 保证,如果发生错误,自动重试3次

Schedulers

Schedulers 抽象化了线程,线程池,GCD中操作队列,Runloop等概念。可以理解为,Schedulers就是一个执行任务的线程。

有一点要注意:默认一个Observerable在其创建的线程上执行

与Schedulers相关的操作符有两个

    observeOn(scheduler) 在一个scheduler上执行任务,使用场景较多
    subscribeOn(scheduler) 在一个scheduler进行监听

比如

sequence1
  .observeOn(backgroundScheduler)
  .map { n in
      print("This is performed on the background scheduler")
  }
  .observeOn(MainScheduler.instance)
  .map { n in
      print("This is performed on the main scheduler")
  }.subscribeOn(backgroundScheduler)
  .subscribeNext{ n in
      print("This is performed on the background scheduler")
  }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

默认一个subscribeNext或者subscribe在其调用的线程上执行
Serial/Concurrent Schedulers 串行或并行

和GCD的队列很相似,并行Schedulers的任务可以并发之行,串行Schedulers只能依次之行。不过RxSwift有内部机制,保证上文提到的信号处理的顺序
RxSwift内置的Scheduler

通常,使用内置的Scheduler足矣。

    CurrentThreadScheduler(串行) 当前线程Scheduler,默认使用的
    MainScheduler(串行) 主线程
    SerialDispatchQueueScheduler 封装了GCD的串行队列
    ConcurrentDispatchQueueScheduler 封装了GCD的并行队列,这个在有任务要在后台执行的时候很有用
    OperationQueueScheduler 封装了NSOperationQueue

例子四,在后台Scheduler之行任务,然后在主线程上更新UI
Variable

Variable表示一个可监听的数据结构。使用Variable,你可以监听数据的变化,也可以把其他值绑定到它身上。

当Variable被释放的时候,它会向监听者发送onCompleted

例子五,Variable进行监听

class VariableController: UIViewController {

    @IBOutlet weak var label: UILabel!
    var timer:NSTimer?
    var count = Variable(0)
    override func viewDidLoad() {
        super.viewDidLoad()
        timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector:#selector(VariableController.updateValue) , userInfo: nil, repeats: true)
        _ = count.asObservable().subscribeNext { (num) in
            self.label?.text = "VariableValue:\(num)"
        }
    }
    func updateValue(){
        count.value = count.value + 1
    }
    override func viewDidDisappear(animated: Bool) {
        super.viewDidDisappear(animated)
        timer?.invalidate()
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

数据绑定

数据绑定是开发的时候很常见的,比如根据文本的输入动态调整textfield的背景色,动态调整按钮的enable。亦或者根据textfield的输入变化,动态的去反馈到model层。如果你听过MVVM,那你肯定知道,MVVM的难点就是ViewModel与View的数据绑定问题。

不过,使用RxSwift,数据绑定变的十分容易,你甚至可以把数据绑定到tableview和collectionView上去。

例子六,bindTo
很简单,随着Switch的开关,view进行显示/隐藏


只需要一行代码

_ = mySwitch.rx_value.bindTo(testView.rx_hidden)

    1

    1

例子七,根据输入,进行View状态绑定

我们想要实现这样的状态

    用户名至少6位,小于6位,则背景色是灰色,合法则透明
    密码至少位8位,小于8位,则背景色是灰色,合法则透明
    当用户名和密码都合法的时候,注册按钮enable,并且背景色变红

信号的处理方式如下,

        let nameObserable = nameTextfield.rx_text.shareReplay(1).map({$0.characters.count >= 6})
        let pwdObserable = passwordTextfield.rx_text.shareReplay(1).map({$0.characters.count >= 8})

        _ = nameObserable.subscribeNext({ (valid) in
            self.nameTextfield.backgroundColor = valid ? UIColor.clearColor():UIColor.lightGrayColor()
        }).addDisposableTo(disposeBag)

        _ = pwdObserable.subscribeNext({ (valid) in
            self.passwordTextfield.backgroundColor = valid ? UIColor.clearColor(): UIColor.lightGrayColor()
        }).addDisposableTo(disposeBag)//addDisposableTo(disposeBag)是为了自动释放

        _ = Observable.combineLatest(nameObserable, pwdObserable) {$0 && $1}.subscribeNext({valid in
                if valid{
                    self.registerButton.enabled = true
                    self.registerButton.backgroundColor = UIColor.redColor()
                }else{
                    self.registerButton.enabled = false
                    self.registerButton.backgroundColor = UIColor.darkGrayColor()
                }
            }).addDisposableTo(disposeBag)
        _ = registerButton.rx_tap.shareReplay(1).subscribeNext {
            print("Button tapped")
        }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

共享监听Sharing subscription-shareReplay

这个是很常用的,比如一个Obserable用做网络请求,通常,当你这样调用的时候,会创建两个序列,也就是会进行两次网络请求,这是不需要的

let network = networkWithText(text)
let subscription1 = network
    .subscribeNext { n in
       //创建第一个序列
    }
let subscription2 = network
    .subscribeNext { n in
       //创建第二个序列
    }

    1
    2
    3
    4
    5
    6
    7
    8
    9

    1
    2
    3
    4
    5
    6
    7
    8
    9

为了共享一个序列,你只需要这这样调用

let network = networkWithText(text).shareReplay(1)

    1

    1

就只会进行一次网络请求,两个subscription共享结果,也就是shareReplay的意思
自定义可绑定属性

上文,textfield和button的状态绑定是手动的,这无疑是不方便的。RxSwift为我们提供了一种方式,来自定义可绑定属性

创建两个exetnsion

extension UITextField{
    var ex_validState:AnyObserver<Bool>{
        return UIBindingObserver(UIElement: self) { textfield, valid in
                textfield.backgroundColor = valid ? UIColor.clearColor():UIColor.lightGrayColor()
            }.asObserver()
    }
}
extension UIButton{
    var ex_validState:AnyObserver<Bool>{
        return UIBindingObserver(UIElement: self) { button, valid in
                button.enabled = valid
            button.backgroundColor = valid ? UIColor.redColor() : UIColor.darkGrayColor()
            }.asObserver()
    }
}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

然后,上文的代码,就可以简化成三行了,So easy

        _ = nameObserable.bindTo(nameTextfield.ex_validState).addDisposableTo(disposeBag)
        _ = pwdObserable.bindTo(passwordTextfield.ex_validState).addDisposableTo(disposeBag)

        _ = Observable.combineLatest(nameObserable, pwdObserable) {$0 && $1}.bindTo(registerButton.ex_validState).addDisposableTo(disposeBag)

    1
    2
    3
    4

    1
    2
    3
    4

Driver

    Driver是RxSwift精心制作的,专门提供给UI层的一个接口。

利用Driver你可以

    利用CoreData的模型来驱动UI
    利用UI的状态来绑定其他UI的状态

Driver能够保证,在主线程上监听,因为UIKit不是需要在主线程上操作


Tips:
RxSwift中做数据绑定有三种

    利用BindTo方法
    利用Driver(强烈建议使用这个,)
    利用KVO来手动绑定(很少用到)

回到Driver上来,上文提到了,对于搜索,我们可以这么做,

let results = query.rx_text
    .throttle(0.3, scheduler: MainScheduler.instance) //延迟0.3秒
    .flatMapLatest { query in //永远只执行最新的
        searchWithText(query)
    }

results
    .map { "\($0.count)" }
    .bindTo(resultCount.rx_text)//绑定label
    .addDisposableTo(disposeBag)

results
    .bindTo(resultsTableView.rx_itemsWithCellIdentifier("Cell")) { (_, result, cell) in //绑定tableview
        cell.textLabel?.text = "\(result)"
    }
    .addDisposableTo(disposeBag)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

那么,这有什么缺陷呢?

    假如searchWithText失败了,那么整个序列就断掉了,后面的绑定不会有任何作用
    假如searchWithText是在后台线程执行的,那么后续绑定是在后台线程上进行的,会崩溃
    绑定了两次,意味着会执行两次

于是,我们需要进行额外的操作,来避免上述缺陷。

let results = query.rx_text
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            .observeOn(MainScheduler.instance)  // 保证在主线程(解决缺陷1)
            .catchErrorJustReturn([])           // 发生错误,返回空数组(解决缺陷2)
                }
    .shareReplay(1)                             // 共享监听,保证只执行一次(解决缺陷3)                                               
results
    .map { "\($0.count)" }
    .bindTo(resultCount.rx_text)
    .addDisposableTo(disposeBag)

results
    .bindTo(resultTableView.rx_itemsWithCellIdentifier("Cell")) { (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .addDisposableTo(disposeBag)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

利用Driver我们可以将上述过程简化

let results = query.rx_text.asDriver()        // 转换成Driver序列
    .throttle(0.3, scheduler: MainScheduler.instance)
    .flatMapLatest { query in
        fetchAutoCompleteItems(query)
            .asDriver(onErrorJustReturn: [])  // 告诉Driver发生错误怎么办
                }

results
    .map { "\($0.count)" }
    .drive(resultCount.rx_text)               // 用Driver绑定,不需要切换到主线程
    .addDisposableTo(disposeBag)                                                            
results
    .drive(resultTableView.rx_itemsWithCellIdentifier("Cell")) { (_, result, cell) in
        cell.textLabel?.text = "\(result)"
    }
    .addDisposableTo(disposeBag)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

任何满足以下三个条件的Observer序列都可以转换为Driver

    不会因为错误就序列断掉(比如,有错误,但是没有调用onError来发送错误)
    在主线程傻姑娘监听
    共享 side effects

对于,使用者只需要调用asDriver(onErrorJustReturn: [])就能保证上述三点都实现了
KVO

通常的KVO,你需要在这个函数里来处理

-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void *)context

    1
    2
    3
    4

    1
    2
    3
    4

而使用RxSwift,KVO变成了这样

view.rx_observe(CGRect.self, "frame")
    .subscribeNext { frame in
        print("Got new frame \(frame)")
    }

    1
    2
    3
    4

    1
    2
    3
    4

或者这样

someSuspiciousViewController
    .rx_observeWeakly(Bool.self, "behavingOk")
    .subscribeNext { behavingOk in
        print("Cats can purr? \(behavingOk)")
    }

    1
    2
    3
    4
    5

    1
    2
    3
    4
    5

二者的区别是

    在rx_observe可以使用地方都可以使用rx_observeWeakly。rx_observeWeakly的执行效率要低一点,因为要处理对象的dealloc关系。除此之外,rx_observeWeakly还可以用在weak属性上。

在使用view.rx_observe的时候,有几点要注意

由于KVO是建立在NSObject子类的基础上的,你可以通过如下方法,来让Structs支持KVO
Notification

使用RxSwift,Notification变的十分简洁

NSNotificationCenter.defaultCenter()
    .rx_notification(UITextViewTextDidBeginEditingNotification, object: myTextView)
    .map { /*do something with data*/ }
    ....

    1
    2
    3
    4

    1
    2
    3
    4

DEBUG
调试编译问题

Swift是一个强类型语言,能够在绝大部分场景自动推断出变量的类型。比如

let  a = 10 //Int

    1

    1

但是,有些时候RxSwift的序列处理会报编译错误
比如

images = word
    .filter { $0.containsString("important") }
    .flatMap { word in
        return self.api.loadFlickrFeed("karate")
            .catchError { error in
                return just(JSON(1))
            }
      }

    1
    2
    3
    4
    5
    6
    7
    8

    1
    2
    3
    4
    5
    6
    7
    8

如果,Swift没办法推断出类型,那么最直接的方式,就是显式的告诉Swift类型是什么

images = word
    .filter { (s: String) -> Bool in s.containsString("important") } //输入是 string ,返回Bool
    .flatMap { (word: String) -> Observable<JSON> in
        return self.api.loadFlickrFeed("karate")
            .catchError { (error: NSError) -> Observable<JSON> in
                return just(JSON(1))
            }
      }

    1
    2
    3
    4
    5
    6
    7
    8

    1
    2
    3
    4
    5
    6
    7
    8

调试

使用Debug操作符,会log所有的数据流

let subscription = myInterval(0.1)
    .debug("my probe")
    .map { e in
        return "This is simply \(e)"
    }
    .subscribeNext { n in
        print(n)
    }

NSThread.sleepForTimeInterval(0.5)


subscription.dispose(

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13

你可以用自定义操作符的方式,来Log

extension ObservableType {
    public func myDebug(identifier: String) -> Observable<Self.E> {
        return Observable.create { observer in
            print("subscribed \(identifier)")
            let subscription = self.subscribe { e in
                print("event \(identifier)  \(e)")
                switch e {
                case .Next(let value):
                    observer.on(.Next(value))

                case .Error(let error):
                    observer.on(.Error(error))

                case .Completed:
                    observer.on(.Completed)
                }
            }
            return AnonymousDisposable {
                   print("disposing \(identifier)")
                   subscription.dispose()
            }
        }
    }
 }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25

调试内存泄漏问题

RxSwift通过RxSwift.resourceCount记录资源分配情况,所以通常的调试方式如下

  /* 在AppDelegate方法中添加Log
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
    */
    _ = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
        .subscribeNext { _ in
        print("Resource count \(RxSwift.resourceCount)")
    }

    1
    2
    3
    4
    5
    6
    7

    1
    2
    3
    4
    5
    6
    7

然后

    进入相关界面,进行正常操作
    退出界面
    观察RxSwift.resourceCount
    在进入同一个界面,退出
    观察RxSwift.resourceCount

使用心得

    时刻牢记,使用RxSwift,尽量把所有的任务(可以理解为方法)抽象成Obserable(序列)和Obserable创建者,监听者
    能用数据绑定的(bindTo和Driver)的就不要手动绑定
    一定要熟练RxSwift提供的操作符,要会自定义操作符

RxSwift的优点

    Composable 可组合,在设计模式中有一种模式叫做组合模式,你可以方便的用不同的组合实现不同的类
    Reusable 代码可重用,原因很简单,对应RxSwift,就是一堆Obserable
    Declarative 响应式的,因为状态不可变,只有数据变化
    Understandable and concise 简洁,容易理解。
    Stable 稳定,因为RxSwift写出的代码,单元测试时分方便
    Less stateful “无”状态性,因为对于响应式编程,你的应用程序就是一堆数据流
    Without leaks 没有泄漏,因为资源管理非常简单
0 0
原创粉丝点击