iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 3
0
Mobile Development

RxSwift / 30天探索之旅系列 第 3

第 3 天 - Observable (上)

  • 分享至 

  • xImage
  •  

嗨,今天講observable。

什麼是Observable?

Rx的一個核心觀念就是observable(或叫sequence ),口訣就是,Everything is a SEQUENCE,大意就是在Rx的世界,都是由observable構成。

Observable的生命週期

在Rx各種教學文件上,都能看到下方的這種示意圖,所以看懂這張圖是今天要講的第一件事!順便也會帶一下常見的英文名詞,往後看到比較不會理解錯誤。
https://ithelp.ithome.com.tw/upload/images/20200917/20115751fkCzF5XOWF.png
圖一、Completed示意圖

https://ithelp.ithome.com.tw/upload/images/20200917/20115751KsZbMNg5KR.png
圖二、Error示意圖

  • 橫的直線:代表時間軸(Time),往右邊箭頭代表時間前進方向
  • 圓圈:代表元素(Element),程式中稱為.next event,observable會向訂閱者發送(emit)元素,元素可以是Int、Void或是自定義的Class。
  • 豎得直線:代表完成(Completed),程式中稱為.completed event,表示對訂閱者說『元素都已經發送完畢了,不在發送任何的事件』。
  • 紅色叉叉:表示錯誤(Error),程式中稱為.error event,表示對訂閱者說『發生錯誤了』,此時,也會停止發送任何的事件。

回過頭來解釋上面第一張圖,我們可以解釋成『向訂閱者發送一個數字1,然後間隔了一下,再發送數字3跟7,然後結束了這個Observable』,第二張圖則是,『 向訂閱者發送數字1、3、7後發生錯誤』。

可以發現到,當發送.completed.error後,Observable就會結束(Terminated),此時任務已完成,資源就會被回收了。所以,光有Observable是沒有任何作用,需要被訂閱(Subscribe),才會開始發送元素!下面我們談談怎麼建立Observable和如何被訂閱。

怎麼建立Observable?

建立一個Observable有許多方式,這裡先用最簡單的方式,使用of建立一個有限序列,of可將多個相同型態的元素,建立成一個Observable

let observable = Observable.of(1, 3, 7)

如何訂閱Observable?

使用.subscribe來訂閱Observable,藉此來接收值得變化

let disposeBag = DisposeBag()

observable
    .subscribe(onNext: { element in
        print(element)
    }, onError: { error in
        print(error)
    }, onCompleted: {
        print("onCompleted")
    }, onDisposed: {
        print("onDisposed")
    })
    .disposed(by: disposeBag)

從程式碼可以對應到我們上面的描述,當訂閱者收到Observable發出的.next、.error、.completed事件時,會依據我們定義好的程式執行。執行結果如下,

1
3
7
onCompleted
onDisposed

Dispose

上個範例有提到Dispose,這是跟Observable銷毀機制有關,上個範例使用的是有限序列,所以元素發送完畢,自然接著發送.completed,但大多是專案開發時,都不是有限序列這麼簡單。

實際狀況可能是『在ViewModel有一個名為Observable<[Order]>來存訂單資料,是藉由呼叫API來取得的,另外在View以TableView呈現,所以,Observable<[Order]>要與TableView DataSource Binding一起,這樣若是重新呼叫API,資料更新,畫面也會更新』,以上的情境下,Observable某種程度算是無限的,所以就不得不考慮何時需要把Observable銷毀,下面我們來談談,Observable的銷毀機制。

想像我們有一個袋子,裡面放著球,當你把這個袋子丟掉,球也一起被丟掉了,從上個例子來看,disposeBag就是那個袋子,訂閱者就是那顆球,我們將訂閱者裝進去,當ViewController deinit時,disposeBag會跟著被銷毀,隨之訂閱者的資源也會一同銷毀,概念就是這麼簡單~

2020.11.02補充,當初在寫本篇,把Dispose跟Observer做了等號,這是不正確的,確切來說裝入袋子的是Disposable,當我們subscribe()時,就會產生一個Disposable,當今天deinit時,Observable卻還沒結束,這時裝在DisposeBag中的所有Disposable就會執行dispose(),藉此來釋放資源,感謝t19960804大大提點

如果你撰寫程式時,進行了.subscribe()卻忘記寫.disposed(),這很有可能會造成Memory leak,這時Swift compiler也會提醒你,在Xcode上會看到黃色的提醒,這時記得加上.disposed(),很貼心吧!

補充:
若想要做實驗,可以使用interval來建立一個『固定間隔時間,發送元素』的Observable

let observable = Observable<Int>.interval(.milliseconds(500), scheduler: MainScheduler.instance)

接著步驟如下:

  1. 在A ViewController畫面做一個按鈕,按下去present到 B ViewController
  2. 在B ViewController建立interval observable,並且訂閱它,列印出.next, .completed, .disposed的變化
  3. 當你按下按鈕時,應該會看到每間隔0.5秒就跑出一個數字
  4. 如果你有寫.disposed(by: disposeBag),當你關閉B ViewController會註銷Observable,應該會觸發onDisposed
  5. 如果你沒寫.disposed(by: disposeBag),當你關閉B ViewController後,仍然會繼續列印出數字,這時恭喜你Memory leak了!

今天介紹了observable的生命週期,如何建立到銷毀,明天還是繼續談Observable的其他的建立方式,就這樣,掰掰


上一篇
第 2 天 - 自學跟推薦資源
下一篇
第 4 天 - Observable (下)
系列文
RxSwift / 30天探索之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
t19960804
iT邦新手 5 級 ‧ 2020-10-30 17:04:43

版主你好
我想請問一下
“Observable的銷毀機制” > disposeBag就是那個袋子,訂閱者就是那顆球,我們將訂閱者裝進去,當ViewController deinit時,disposeBag會跟著被銷毀

為什麼被銷毀的卻是訂閱者?

Bing iT邦新手 5 級 ‧ 2020-11-02 21:33:18 檢舉

感謝大大留言,對此,我重新再看了一下Disposable

這邊講得不太對,不應該說是把訂閱者裝進袋子,確切來說裝入袋子的是Disposable,當我們subscribe()時,就會產生一個Disposable,當今天deinit時,Observable卻還沒結束,這時裝在DisposeBag中的所有Disposable就會執行dispose(),來釋放資源

舉個例子說,NotificationCenter+Rx.swift中,Observable.create {}會建立並回傳一個Disposable,closure當中就會撰寫需要當結束的時候要做什麼事情,當Observable順利結束會執行這closure,或是突然提前結束,DisposeBag也會執行這closure。

若仍有誤再麻煩補充or討論(鞠躬

P.S.當初寫的時候,把Dispose跟Observer做了等號,現在回頭看是不正確的,在原文加以補充

t19960804 iT邦新手 5 級 ‧ 2020-11-03 11:07:27 檢舉

感謝版主回覆
所以Disposable = 被subscribe的Observerable
這樣理解是不是就符合“Observable的銷毀機制”這個標題?

Bing iT邦新手 5 級 ‧ 2020-11-04 16:11:09 檢舉

直接等號似乎怪怪的,比較像是訂閱後的產物

Disposable = Observerable被subscribe後所產生的產物,用來當Observable提前結束時可用來釋放資源

這樣就符合銷毀機制這標題

我要留言

立即登入留言