ReactiveSwift框架分析1 — Signal 和 Observer

為什么用ReactiveSwift?

先看個(gè)例子呈础,假設(shè)我們想要實(shí)現(xiàn)一個(gè)用戶界面舆驶,其中包含一個(gè)UILabel (讓我們稱之為label )和一個(gè)UITextView (讓我們稱之為textView )橱健,其中UILabel需要實(shí)時(shí)地顯示出在UITextView中輸入的文本。 為了實(shí)現(xiàn)這種需求贞远,我們一般會監(jiān)聽textView的代理方法:

func textViewDidChange(_ textView: UITextView) { 
  label.text = textView.text 
 }

而在ReactiveSwift畴博,我們可以很優(yōu)雅地讓label的text跟textView的text實(shí)時(shí)保持一致:

label.reactive.text <~ textView.reactive.continuousTextValues

簡簡單單地通過操作符<?,就將label的text跟textView的text進(jìn)行了綁定蓝仲,label的文本會在label的整個(gè)生命周期中與textView的文本等同。

<?稱為操作符(binding operator)官疲,操作符的左側(cè)是綁定目標(biāo)(binding target) 袱结,右側(cè)是綁定源(binding source)。

再來看一個(gè)例子:

usernameTextField.reactive.continuousTextValues.observeValues {
    text in
            
    print(text ?? "")
 }

隨著username text field輸入途凫,你在Xcode控制臺會看到以下輸出:

e
ee
eef
eeff
eefff
eeffff
eefffff
eeffffff

每次你在username text field輸入垢夹,控制臺都會輸出,而不用設(shè)置target-action维费,也不用設(shè)置委托(delegate)果元。

ReactiveCocoa信號會發(fā)送一系列的事件給訂閱者(觀察者)。ReactiveCocoa有以下事件:

  • Value事件:Value事件提供了新值
  • Failed事件:Failed事件表明在信號完成之前發(fā)生了錯誤
  • Completed事件:Completed事件信號完成了犀盟,之后不會再有新的值發(fā)送
  • Interrupted事件:Interrupted事件表明由于被取消而晒,信號被終止了

usernameTextField.reactive就是把usernameTextField變成可響應(yīng)的,而continuousTextValues就是text值的信號阅畴。通過observeValues倡怎,我們可以觀察到continuousTextValues這個(gè)信號傳來的Value事件,每次在usernameTextField輸入的時(shí)候贱枣,我們就會接收到text的值监署。

如何創(chuàng)建一個(gè)Signal并通過Observer觀察它呢?

  1. 信號Signal

信號被定義為事件流纽哥,其中每個(gè)事件表示在指定時(shí)間點(diǎn)上的一種狀態(tài)钠乏。如我們在文本框輸入的時(shí)候,它會不斷地產(chǎn)生值類型的事件春塌。事件event是信號的基本單位晓避,它是一個(gè)枚舉:

public enum Event {
        /// A value provided by the signal.
        case value(Value)

        /// The signal terminated because of an error. No further events will be
        /// received.
        case failed(Error)

        /// The signal successfully terminated. No further events will be received.
        case completed

        /// Event production on the signal has been interrupted. No further events
        /// will be received.
        ///
        /// - important: This event does not signify the successful or failed
        ///              completion of the signal.
        case interrupted
}
  1. 觀察者Observer

在ReactiveSwift中我們可以通過Observer觀察Signal發(fā)出的事件。Observer封裝了一個(gè)閉包(Event) -> Void摔笤,我們可以在閉包中根據(jù)event類型進(jìn)行相應(yīng)的處理:

let observer = Signal<Int, NoError>.Observer { (event) in
    switch event {
    case let .value(v):
        print("value = \(v)")
    case let .failed(error):
        print("error = \(error)")
    case .completed:
        print("completed")
    case .interrupted:
        print("interrupted")
    }
}

現(xiàn)在够滑,我們清楚如何創(chuàng)建一個(gè)Observer,接下來通過一個(gè)例子看看如何創(chuàng)建信號及觀察信號吕世。

要求:每隔五秒鐘打印一次彰触,持續(xù)五十秒。

信號的創(chuàng)建可以看作是流過管道的水流命辖,Observer在源端不斷地發(fā)送數(shù)據(jù)况毅,將事件注入到輸出端Signal分蓖,然后我們在通過跟Signal綁定一起的Observer實(shí)例觀察到Signal中的value,其實(shí)整個(gè)過程就這么簡單尔许,so easy么鹤!

首先,創(chuàng)建信號:

let (output, input) = Signal<Int, NoError>.pipe()

通過pipe()方法味廊,創(chuàng)建一個(gè)‘管道’蒸甜,這個(gè)‘管道’其實(shí)是個(gè)元組,元素output代表輸出端余佛,類型為Signal <Int柠新,NoError> , 元素input代表輸入端辉巡,類型為Observer <Int恨憎,NoError>,作為input的Observer發(fā)送數(shù)據(jù)郊楣,將事件注入output的Signal憔恳。

現(xiàn)在我們向信號發(fā)送值:

for i in 0..<10 {
 DispatchQueue.main.asyncAfter(deadline: .now() + 5.0 * Double(i)) {
     input.send(value: i)
 }
}

好了,信號有了净蚤,我們還需要一個(gè)Observer實(shí)例來觀察信號中的事件钥组,上面我說過,Observer封裝了event的處理邏輯塞栅,現(xiàn)在我們需要把前面發(fā)送的值打印出來者铜,所以我們創(chuàng)建了這樣的一個(gè)Observer:

let signalObserver =  Signal<Int, NoError>.Observer (
value: { value in
    print("Time elapsed = \(value)")
}, 
completed: { print("completed") },
interrupted: { print("interrupted") })
output.observe(signalObserver)

完整的代碼如下:

//Create an observer
let signalObserver = Signal<Int, NoError>.Observer(
value: { value in
    print("Time elapsed = \(value)")
}, completed: { 
    print("completed") 
}, interrupted: { 
    print("interrupted") 
})
//Create an a signal
let (output, input) = Signal<Int, NoError>.pipe()
//Send value to signal
for i in 0..<10 {
    DispatchQueue.main.asyncAfter(deadline: .now() + 5.0 *  Double(i)) {
  input.send(value: i)
 }
}
//Observe the signal
output.observe(signalObserver)

再來看個(gè)例子:

當(dāng)文本框輸入字符超過10個(gè)時(shí),啟用按鈕放椰。

第1步:創(chuàng)建信號

let signal = textField.reactive.continuousTextValues 

這里作烟,在文本textfield鍵入的文本通過信號textField.reactive.continuousTextValues表示。

第2步:轉(zhuǎn)換信號

在第一步中創(chuàng)建的信號會發(fā)出可選字符串砾医。 我們可以通過運(yùn)算符map將其轉(zhuǎn)換成發(fā)出布爾值的信號拿撩。

 let transformedSignal = signal 
  .map {text in 
 text ??  “”} 
  .map {text in 
  text.characters.count> 10 
  } 

第3步:觀察信號

接下來,我們將觀察信號如蚜,并將設(shè)置按鈕的isEnabled的操作封裝到Observer中压恒。

let observer = Signal<Bool, NoError>.Observer(value: { value in
     button.isEnabled = value
})
let disposable = transformedSignal.observe(observer)

第4步:停止觀察信號

disposable?.dispose()

當(dāng)然,最好的做法是在類的deinit中釋放所有觀察者错邦。

以上整個(gè)過程是:

//Defining consumer
let observer = Signal<Bool, NoError>.Observer(value: { value in
    button.isEnabled = value
})
//Defining source
let signal = textField.reactive.continuousTextValues
let transformedSignal = signal
.map { text in
   text ?? "" }
.map { text in
   text.characters.count > 10
}
//Consuming signal
let disposable = transformedSignal.observe(observer)
//Limit Scope
disposable?.dispose()

小結(jié):

讓我們回顧一下探赫,我們需要三個(gè)簡單的步驟來創(chuàng)建一個(gè)信號并觀察它:

  1. 創(chuàng)建一個(gè)pipe管道,同時(shí)創(chuàng)建了一個(gè)輸入端Observer撬呢,一個(gè)輸出端Signal
  2. 創(chuàng)建觀察者伦吠,訂閱Signal。
  3. 通過輸入端Observer發(fā)送值給Signal,這將觸發(fā)所有訂閱了該信號的觀察者執(zhí)行與觀察者關(guān)聯(lián)的閉包毛仪。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末搁嗓,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子箱靴,更是在濱河造成了極大的恐慌腺逛,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衡怀,死亡現(xiàn)場離奇詭異棍矛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)狈癞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門茄靠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蝶桶,你說我怎么就攤上這事〉粢保” “怎么了真竖?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長厌小。 經(jīng)常有香客問我恢共,道長,這世上最難降的妖魔是什么璧亚? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任讨韭,我火速辦了婚禮,結(jié)果婚禮上癣蟋,老公的妹妹穿的比我還像新娘透硝。我一直安慰自己,他們只是感情好疯搅,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布濒生。 她就那樣靜靜地躺著,像睡著了一般幔欧。 火紅的嫁衣襯著肌膚如雪罪治。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天礁蔗,我揣著相機(jī)與錄音觉义,去河邊找鬼。 笑死浴井,一個(gè)胖子當(dāng)著我的面吹牛晒骇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼厉碟,長吁一口氣:“原來是場噩夢啊……” “哼喊巍!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起箍鼓,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤崭参,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后款咖,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體何暮,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年铐殃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了海洼。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡富腊,死狀恐怖坏逢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情赘被,我是刑警寧澤是整,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站民假,受9級特大地震影響浮入,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜羊异,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一事秀、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧野舶,春花似錦易迹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至巢掺,卻和暖如春句伶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背陆淀。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工考余, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人轧苫。 一個(gè)月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓楚堤,卻偏偏與公主長得像疫蔓,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子身冬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容