ReactiveCocoa 入門(mén)教程一 (Swift 版本)

ReactiveCocoa
ReactiveCocoa

在編寫(xiě)iOS代碼時(shí),我們的大部分代碼都是在響應(yīng)一些事件:按鈕點(diǎn)擊、接收網(wǎng)絡(luò)消息、屬性變化等等。但是這些事件在代碼中的表現(xiàn)形式卻不一樣:如target-action、代理方法、KVO、回調(diào)或其它。ReactiveCocoa的目的就是定義一個(gè)統(tǒng)一的事件處理接口,這樣它們可以非常簡(jiǎn)單地進(jìn)行鏈接、過(guò)濾和組合。
ReactiveCocoa結(jié)合了一些編程模式:
1 函數(shù)式編程:利用高階函數(shù),即將函數(shù)作為其它函數(shù)的參數(shù)。
2 響應(yīng)式編程:關(guān)注于數(shù)據(jù)流及變化的傳播。
基于以上兩點(diǎn),ReactiveCocoa被當(dāng)成是函數(shù)響應(yīng)編程(Functional Reactive Programming, FRP)框架。我們將在下面以實(shí)例來(lái)看看ReactiveCocoa的實(shí)用價(jià)值。

添加ReactiveCocoa框架

添加ReactiveCocoa框架到我們工程的最簡(jiǎn)單的方法是使用Cocoapods。我們先關(guān)閉ReactivePlayground工程。Cocoapods會(huì)創(chuàng)建一個(gè)Xcode workspace,它會(huì)替代我們的原始工程文件。
首先創(chuàng)建一個(gè)名為Podfile的空文件,打開(kāi)并添加如下信息:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

pod 'ReactiveCocoa'

配置完成后保存文件,打開(kāi)終端并轉(zhuǎn)到工程所在目錄,然后輸入以下命令:

pod install --verboser --no-repo-update

安裝完成后,Cocoapods會(huì)創(chuàng)建了一個(gè)Xcode workspace,同時(shí)將框架整合到了我們的工程中。打開(kāi)新生成的workspace文件(RWReactivePlayground.xcworkspace),雙擊運(yùn)行workspace打開(kāi)工程
如果

** 注意:swift安裝好pod第三方庫(kù)之后一般需要手動(dòng)添加相應(yīng)的.framework;添加位置在工程的general > Linked Frameworks and Libraries 或者在 Build Phasses > Link Binary With Libraries | 如下圖: **


QQ20160416-1@2x
QQ20160416-1@2x


QQ20160416-2@2x
QQ20160416-2@2x

QQ20160416-3@2x
QQ20160416-3@2x

開(kāi)始使用

經(jīng)過(guò)上面的步驟,我們的環(huán)境應(yīng)該已經(jīng)搭建完成了。
接下來(lái)開(kāi)始使用ReactiveCocoa
首先打開(kāi)我們默認(rèn)的ViewController.swift,在頂部引用ReactiveCocoa庫(kù)

import ReactiveCocoa

然后我們?cè)趘iewdidload中創(chuàng)建一個(gè)TextField,并且使用ReactiveCocoa監(jiān)聽(tīng)textfiled值變化事件

override func viewDidLoad() {
        super.viewDidLoad()
        
        let tv1 = UITextField(frame: CGRect(x: 50, y: 50, width: 200, height: 30))
        tv1.backgroundColor = UIColor(red: 0.9, green: 0.9, blue: 0.9, alpha: 1)
        tv1.placeholder = "用戶(hù)名"
        view.addSubview(tv1)
        
        tv1.rac_textSignal().subscribeNext { (text) in
            print(text)
        }
    }

運(yùn)行程序,我們會(huì)看到輸出結(jié)果(我們每一個(gè)輸入鍵入后都會(huì)調(diào)用到這個(gè)事件,并且返回當(dāng)前textfield的值):

D
Ds
Dsa
Dsad
Dsada
Dsadas
Dsadasd
Dsadasds
Dsadasdsa

我們可以看到,每次在text field中輸入時(shí),都會(huì)執(zhí)行block中的代碼。沒(méi)有target-action,沒(méi)有代理,只有信號(hào)與block。是不是很棒?
ReactiveCocoa信號(hào)發(fā)送一個(gè)事件流到它們的訂閱者中。我們需要知道三種類(lèi)型的事件:next, error和completed。一個(gè)信號(hào)可能由于error事件或completed事件而終止,在此之前它會(huì)發(fā)送很多個(gè)next事件。在這一部分中,我們將重點(diǎn)關(guān)注next事件。在學(xué)習(xí)關(guān)于error和completed事件前,請(qǐng)仔細(xì)閱讀第二部分。
RACSignal有許多方法用于訂閱這些不同的事件類(lèi)型。每個(gè)方法會(huì)有一個(gè)或多個(gè)block,每個(gè)block執(zhí)行不同的邏輯處理。在上面這個(gè)例子中,我們看到subscribeNext:方法提供了一個(gè)響應(yīng)next事件的block。
ReactiveCocoa框架通過(guò)類(lèi)別來(lái)為大部分標(biāo)準(zhǔn)UIKit控件添加信號(hào),以便這些控件可以添加其相應(yīng)事件的訂閱,如上面的UITextField包含了rac_textSignal屬性。
理論講得差不多了,我們繼續(xù)吧?。?!

ReactiveCocoa有大量的操作右用于處理事件流。例如,如果我們只對(duì)長(zhǎng)度大于3的用戶(hù)名感興趣,則我們可以使用filter操作。在viewDidLoad中更新我們的代碼如下:

tv1.rac_textSignal().filter { (text) -> Bool in
            return text.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) > 3
        } .subscribeNext { (text) in
            print(text)
        }

運(yùn)行并在用戶(hù)名輸入框中輸入"reactive cocoa",我們可以看到控制臺(tái)會(huì)有如下輸出:

reac
react
reacti
reactiv
reactive
reactive
reactive c
reactive co
reactive coc
reactive coco
reactive cocoa

可以看到當(dāng)長(zhǎng)度小于3時(shí),并不執(zhí)行后續(xù)的操作。通過(guò)這種方式,我們創(chuàng)建了一個(gè)簡(jiǎn)單的管道。這就是響應(yīng)式編程的實(shí)質(zhì),我們將我們程序的功能表示為數(shù)據(jù)流的形式。我們可以將上述調(diào)用表示為以下圖例:


圖片示例
圖片示例

從上圖中我們可以看到rac_textSignal是事件的初始源頭。通過(guò)filter的數(shù)據(jù)流只有在其長(zhǎng)度大于3時(shí),才會(huì)被傳遞到下一處理流程中。管道的最后一步是subscribeNext:,在這個(gè)block中,我們記錄日志。
在這里需要注意的是filter操作的輸出仍然是一個(gè)RACSignal對(duì)象。我們可以將上面這段管道處理拆分成如下代碼:

let usernameSourceSignal = tv1.rac_textSignal();
        let filteredUsername = usernameSourceSignal.filter { (text) -> Bool in
            return text.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) > 3
        }
        filteredUsername.subscribeNext { (text) in
            print(text)
        }

因?yàn)镽ACSignal對(duì)象的每個(gè)操作都返回一個(gè)RACSignal對(duì)象,所以我們不需要使用變量就可以構(gòu)建一個(gè)管道。

事件是什么

目前為止,我們已經(jīng)描述了3種不同的事件類(lèi)型,但還沒(méi)有深入這些事件的結(jié)構(gòu)。有趣的是,事件可以包含任何東西。為了證明這一點(diǎn),我們?cè)谏厦娴墓艿乐屑尤肓硪粋€(gè)操作。更新我們的代碼:

tv1.rac_textSignal().map { (text) -> AnyObject! in
            return text.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
        }.filter { (length) -> Bool in
            return length as! Int > 3
        } .subscribeNext { (length) in
            print(length)
        }

編譯并運(yùn)行,我們會(huì)發(fā)現(xiàn)控制臺(tái)輸出如下信息:

4
5
6
7
8
9
10
11
12

新添加的map操作使用提供的block來(lái)轉(zhuǎn)換事件數(shù)據(jù)。對(duì)于收到的每一個(gè)next事件,都會(huì)運(yùn)行給定的block,并將返回值作為next事件發(fā)送。在上面的代碼中,map操作獲取一個(gè)NSString輸入,并將其映射為一個(gè)NSNumber對(duì)象,并返回。下圖演示了這個(gè)管道處理:



我們可以看到,map操作后的每一步接收的都是一個(gè)NSNumber對(duì)象。我們可以使用map操作來(lái)轉(zhuǎn)換我們想要的數(shù)據(jù),只需要它是一個(gè)對(duì)象。

好了,今天的內(nèi)容暫時(shí)就寫(xiě)這么多,請(qǐng)大家繼續(xù)關(guān)注我的頭條號(hào),也可以訪(fǎng)問(wèn)我的個(gè)人博客

參考作者

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容