來聊聊有共享作用的監聽序列
前言
在上一篇描述了三種不會共享事件的監聽序列,接下來會針對會共享監聽序列的Driver、Signal、ControlEvent做主要描述,在這一部分,有一些小特點可以稍微提一下Driver、Signal、ControlEvent有一些共通的特性如下:
- 不會產生Error事件
- 都在主線程MainScheduler進行監聽
- 會有共享附加作用
但都有同樣特性的監聽序列,為什麼要分成三種呢?這邊可能需要一一解析一下不同監聽序列的使用情境。
Driver
Driver的特點如下:
- 會對訂閱的觀察者回放上一個元素
- 主要是針對UI使用
但也許我們在這裡可以問一個問題:為什麼要對觀察者回放上一個元素呢?有何意義?
我們以下舉的例子是官方文檔中,沒有使用Driver的例子:
我們藉由textField輸入一個關鍵字,然後進行網路請求後,得到結果,這個結果會顯示在tableView,而數量會顯示在Label上,如下圖:

不過官方文檔這裡也指出,如果是上述的做法,會有幾個問題出現:
If the
fetchAutoCompleteItems
observable sequence errors out (connection failed or parsing error), this error would unbind everything and the UI wouldn't respond any more to new queries.If
fetchAutoCompleteItems
returns results on some background thread, results would be bound to UI elements from a background thread which could cause non-deterministic crashes.Results are bound to two UI elements, which means that for each user query, two HTTP requests would be made, one for each UI element, which is not the intended behavior.
如果fetchAutoCompleteItems的觀察序列出現error,無論是連接失敗或是格式問題,這樣會很難去追溯問題的根源,因為UI並不會給出任何的反饋。
如果是在背景的執行序執行,就算UI的更新也會在後台,這樣還是會造成crash。
若是結果會與兩種UI的元素綁定,這樣意味著任何一種使用者的請求,都會為UI發起兩種的請求,這樣與預期的行為並不同。
所以,官方文檔提出以下這樣的改變:

請求的資料回傳會回到主線程,而避開背景執行緒,然後藉由.catchErrorJustReturn讓錯誤的元素可以被保存。
接下來,我們使用Driver來處理上述的邏輯:

在Code裡面,我們把一般的ControlProperties轉成了Driver序列,在官方文檔裡指出:「Any observable sequence can be converted to Driver
trait, as long as it satisfies 3 properties」
任何的序列都可以被轉成Driver序列,只要滿足三個條件:
- Can’t error out.(不會產生error事件)
- Observe on main scheduler.(在主線程上觀察序列)
- Sharing side effects (
share(replay: 1, scope: .whileConnected)
).(共享副作用)
不過,所以Driver在做什麼用,或許我們可以更實際的用,下面這個例子來看:

在instanceView中有三個物件:
- nameLabel
- numberLabel
- textField
實際透過輸入名字,你會看到Driver會對numberLabel回放上一個元素,所以,這是一個不錯的例子去演示Driver的用法。

但假如今天換成是點擊事件呢?我們來看實際的Code會長怎麼樣?


所以,在這裡點擊一個按鈕事件之後,創建一個新的觀察者,來響應事件,那大家還記得Driver的用處嗎?
Driver會把上一個點擊的事件傳給新的觀察者,所以,當newObserver訂閱時,就會接受到上次執行的點擊事件,然後彈框。

所以,你會在consoleLog中看到Driver會一直回放上一個事件。
Signal
- 不會對訂閱的觀察者重新發送上一個元素
- 不會產生error事件
- 一定在主線程(MainScheduler)監聽
在上一個用Driver點擊按鈕驅動的事件中,新的觀察者是接受「上一個回放的事件」,然後再彈出視窗,這件事情似乎不合理。於是我們在這裡引入了Signal。
還記得上面對Signal的描述是不會對訂閱的觀察者發送上一個元素嗎?
在這裏我們會使用類似的程式碼去執行如下:

實際執行我們看consoleLog:
- 點按第一次Button

2. 點按第二次Button

3. 點按第三次以及第四次Button

你會看到的是,Signal會讓事件單一的執行,並不會回放,這點跟Driver的行為並不一樣。
ControlEvent
據描述,是主要UI物件的所產生的事件,所以除了在前言上描述的三個特點,在主線程MainScheduler裡進行訂閱也是很重要的一個特色。
不過,如果把ControlEvent放在這裡討論,恐怕會缺少一些對比的對象,但我們還是根據讀到的資源,稍微重複提點一下ControlEvent的特色:
- 不會產生error事件
- 一定會在MainScheduler裡訂閱與監聽
那麼接下來我們會提到更多不一樣的觀察序列,或者回過頭來詮釋跟ControlEvent相近的概念。
我們下一篇見!