ReactiveCocoa入門看這一篇就夠了

簡介

ReactiveCocoa 在GitHub有1.5萬多個星恤浪,不少大型公司的的都用它作為主流框架,比如美團蒙保,但它同時又是一個非常復雜的框架蠢古,在正式開始介紹它的核心組件前奴曙,我們先來看看它的類圖,以便從宏觀上了解它的層次結(jié)構(gòu):

介紹

從上面的類圖中草讶,我們可以看出洽糟,ReactiveCocoa 主要由以下四大核心組件構(gòu)成:

  • 信號源:RACStream 及其子類;
  • 訂閱者:RACSubscriber 的實現(xiàn)類及其子類堕战;
  • 調(diào)度器:RACScheduler 及其子類坤溃;
  • 清潔工:RACDisposable 及其子類。

ReactiveCocoa作用

在我們iOS開發(fā)過程中嘱丢,當某些事件響應的時候薪介,需要處理某些業(yè)務邏輯,這些事件都用不同的方式來處理。
比如按鈕的點擊使用action屿讽,ScrollView滾動使用delegate昭灵,屬性值改變使用KVO等系統(tǒng)提供的方式。

其實這些事件伐谈,都可以通過RAC處理
ReactiveCocoa為事件提供了很多處理方法烂完,而且利用RAC處理事件很方便,可以把要處理的事情诵棵,和監(jiān)聽的事情的代碼放在一起抠蚣,這樣非常方便我們管理,就不需要跳到對應的方法里履澳。非常符合我們開發(fā)中高聚合嘶窄,低耦合的思想

對于一個應用來說,絕大部分的時間都是在等待某些事件的發(fā)生或響應某些狀態(tài)的變化距贷,比如用戶的觸摸事件柄冲、應用進入后臺、網(wǎng)絡請求成功刷新界面等等忠蝗,而維護這些狀態(tài)的變化现横,常常會使代碼變得非常復雜,難以擴展阁最。而 ReactiveCocoa 給出了一種非常好的解決方案戒祠,它使用信號來代表這些異步事件,提供了一種統(tǒng)一的方式來處理所有異步的行為速种,包括代理方法姜盈、block 回調(diào)、target-action 機制配阵、通知馏颂、KVO 等:


// 代理方法
[[self
    rac_signalForSelector:@selector(webViewDidStartLoad:)
    fromProtocol:@protocol(UIWebViewDelegate)]
    subscribeNext:^(id x) {
        // 實現(xiàn) webViewDidStartLoad: 代理方法
    }];

// target-action
[[self.avatarButton
    rac_signalForControlEvents:UIControlEventTouchUpInside]
    subscribeNext:^(UIButton *avatarButton) {
        // avatarButton 被點擊了
    }];

// 通知
[[[NSNotificationCenter defaultCenter]
    rac_addObserverForName:kReachabilityChangedNotification object:nil]
    subscribeNext:^(NSNotification *notification) {
        // 收到 kReachabilityChangedNotification 通知
    }];

// KVO
[RACObserve(self, username) subscribeNext:^(NSString *username) {
    // 用戶名發(fā)生了變化
}];

然而示血,這些還只是 ReactiveCocoa 的冰山一角,它真正強大的地方在于我們可以對這些不同的信號進行任意地組合和鏈式操作饱亮,從最原始的輸入 input 開始直至得到最終的輸出 output 為止:

[[[RACSignal
    combineLatest:@[ RACObserve(self, username), RACObserve(self, password) ]
    reduce:^(NSString *username, NSString *password) {
      return @(username.length > 0 && password.length > 0);
    }]
    distinctUntilChanged]
    subscribeNext:^(NSNumber *valid) {
        if (valid.boolValue) {
            // 用戶名和密碼合法矾芙,登錄按鈕可用
        } else {
            // 用戶名或密碼不合法舍沙,登錄按鈕不可用
        }
    }];

RACSignal(信號源)

RACSignal 代表的是未來將會被傳送的值近上,它是一種 push-driven 的流。RACSignal 可以向訂閱者發(fā)送三種不同類型的事件:

  • next :RACSignal 通過 next 事件向訂閱者傳送新的值拂铡,并且這個值可以為 nil 壹无;
  • error :RACSignal 通過 error 事件向訂閱者表明信號在正常結(jié)束前發(fā)生了錯誤;
  • completed :RACSignal 通過 completed 事件向訂閱者表明信號已經(jīng)正常結(jié)束感帅,不會再有后續(xù)的值傳送給訂閱者斗锭。

注意,ReactiveCocoa中的值流只包含正常的值失球,即通過 next 事件傳送的值岖是,并不包括 errorcompleted 事件,它們需要被特殊處理实苞。通常情況下豺撑,一個信號的生命周期是由任意個 next 事件和一個 error 事件或一個 completed 事件組成的

RACSubscriber(訂閱者)

現(xiàn)在,我們已經(jīng)知道信號源是什么了黔牵,為了獲取信號源中的值聪轿,我們需要對信號源進行訂閱。在 ReactiveCocoa 中猾浦,訂閱者是一個抽象的概念陆错,所有實現(xiàn)了 RACSubscriber 協(xié)議的類都可以作為信號源的訂閱者

其中 -sendNext: 、-sendError: 和 -sendCompleted 分別用來從 RACSignal 接收 next 金赦、error 和 completed 事件音瓷,而 -didSubscribeWithDisposable: 則用來接收代表某次訂閱的 disposable 對象。

訂閱者對信號源的一次訂閱過程可以抽象為:通過 RACSignal 的 -subscribe: 方法傳入一個訂閱者夹抗,并最終返回一個 RACDisposable 對象的過程:

subscribe.png

)

RACScheduler(調(diào)度器)

有了信號源和訂閱者绳慎,我們還需要由調(diào)度器來統(tǒng)一調(diào)度訂閱者訂閱信號源的過程中所涉及到的任務,這樣才能保證所有的任務都能夠合理有序地執(zhí)行

RACSchedulerReactiveCocoa 中就是扮演著調(diào)度器的角色兔朦,本質(zhì)上偷线,它就是用 GCD 的串行隊列來實現(xiàn)的,并且支持取消操作沽甥。是的声邦,在 ReactiveCocoa 中,并沒有使用到 NSOperationQueueNSRunloop 等技術(shù)摆舟,RACScheduler 也只是對 GCD 的簡單封裝而已

RACDisposable(清潔工)

正如我們前面所說的亥曹,在訂閱者訂閱信號源的過程中邓了,可能會產(chǎn)生副作用或者消耗一定的資源,所以當我們在取消訂閱或者完成訂閱時媳瞪,我們就需要做一些資源回收和垃圾清理的工作

RACDisposableReactiveCocoa 中就充當著清潔工的角色骗炉,它封裝了取消和清理一次訂閱所必需的工作。它有一個核心的方法 -dispose 蛇受,調(diào)用這個方法就會執(zhí)行相應的清理工作句葵,這有點類似于 NSObject-dealloc 方法。RACDisposable 總共有四個子類兢仰,它的繼承結(jié)構(gòu)圖如下

RACDisposable.jpg
  • RACSerialDisposable :作為 disposable 的容器使用乍丈,可以包含一個 disposable 對象,并且允許將這個 disposable 對象通過原子操作交換出來把将;
  • RACKVOTrampoline :代表一次 KVO 觀察轻专,并且可以用來停止觀察;
  • RACCompoundDisposable :跟 RACSerialDisposable 一樣察蹲,* RACCompoundDisposable 也是作為 disposable 的容器使用请垛。不同的是,它可以包含多個 disposable 對象洽议,并且支持手動添加和移除 disposable 對象宗收,有點類似于可變數(shù)組 NSMutableArray 。而當一個 RACCompoundDisposable 對象被 disposed 時绞铃,它會調(diào)用其所包含的所有 disposable 對象的 -dispose 方法镜雨,有點類似于 autoreleasepool 的作用;
  • RACScopedDisposable :當它被 dealloc 的時候調(diào)用本身的 -dispose 方法

=========基礎知識解釋==========

  1. RACSubject:RACSubject:信號提供者,自己可以充當信號儿捧,又能發(fā)送信號荚坞。

使用場景:通常用來代替代理,有了它菲盾,就不必要定義代理了颓影。

RACReplaySubject:重復提供信號類,RACSubject的子類懒鉴。

RACReplaySubjectRACSubject區(qū)別:

RACReplaySubject可以先發(fā)送信號诡挂,在訂閱信號,RACSubject就不可以临谱。

使用場景一:如果一個信號每被訂閱一次璃俗,就需要把之前的值重復發(fā)送一遍,使用重復提供信號類悉默。

使用場景二:可以設置capacity數(shù)量來限制緩存的value的數(shù)量,即只緩充最新的幾個值城豁。

  1. RACTuple:元組類,類似NSArray,用來包裝值

  2. RACSequence:RAC中的集合類,用于代替NSArray,NSDictionary,可以使用它來快速遍歷數(shù)組和字典

  3. RACCommand:RAC中用于處理事件的類抄课,可以把事件如何處理,事件中的數(shù)據(jù)如何傳遞唱星,包裝到這個類中雳旅,他可以很方便的監(jiān)控事件的執(zhí)行過程。

  • 使用場景:監(jiān)聽按鈕點擊间聊,網(wǎng)絡請求
  1. RACScheduler:RAC中的隊列攒盈,用GCD封裝的

  2. 代替代理: rac_signalForSelector:用于替代代理。

  3. 代替KVO : rac_valuesAndChangesForKeyPath:用于監(jiān)聽某個對象的屬性改變哎榴。

  4. 監(jiān)聽事件: rac_signalForControlEvents:用于監(jiān)聽某個事件型豁。

  5. 代替通知: rac_addObserverForName:用于監(jiān)聽某個通知。

  6. 監(jiān)聽文本框文字改變: rac_textSignal:只要文本框發(fā)出改變就會發(fā)出這個信號叹话。

  7. 處理當界面有多次請求時偷遗,需要都獲取到數(shù)據(jù)時,才能展示界面

rac_liftSelector:withSignalsFromArray:Signals:當傳入的Signals(信號數(shù)組)驼壶,每一個signal都至少sendNext過一次,就會去觸發(fā)第一個selector參數(shù)的方法喉酌。
使用注意:幾個信號热凹,參數(shù)一的方法就幾個參數(shù),每個參數(shù)對應信號發(fā)出的數(shù)據(jù)泪电。

操作符用法解釋

一. flattenMap 和 map的區(qū)別

Map:用于把原信號中的內(nèi)容映射成新的內(nèi)容
flattenMap的作用:把原信號的內(nèi)容映射成一個新的信號般妙,信號可以是任意的類型</br>

1.FlatternMap中的Block返回信號

2.Map中的Block返回對象

3.開發(fā)中,如果信號發(fā)出的值不是信號相速,映射一般使用Map

4.開發(fā)中碟渺,如果信號發(fā)出的值是信號,映射一般使用FlatternMap

   RACSequence* numbers =self.dataArrNumber.rac_sequence;
    
    // Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
 
    RACSequence* extended = [numbers flattenMap:^RACSequence *(NSString* num) {
        
        return@[num,num].rac_sequence;
        
    }];
     [[extended signal] subscribeNext:^(NSString* text) {
    
            [self logSource:@"extended" text:text];
    
        }];

flatten

就像一條流水線突诬,將源信號一個一個發(fā)出去,也可以理解成把幾個水管的龍頭合并成一個苫拍,按照順序連接

contact

按一定順序拼接信號,當多個信號發(fā)出的時候有順序的接受信號

    RACSubject *subjectA = [RACSubject subject];

   RACSubject *subjectB = [RACSubject subject];

   //把subjectA拼接到subjectB的時候只有subjectA發(fā)送完畢之后subjectB才會被激活

   // 只需要訂閱拼接之后的信號桩卵,不在需要單獨拼接subjectA或者subjectB,內(nèi)部會自動訂閱

   [[subjectA concat:subjectB] subscribeNext:^(id x) {

       NSLog(@"%@",x);

   }];

   [subjectA sendNext:@"subjectA發(fā)送完信號"];

   // 第一個信號發(fā)送完成合溺,第二個信號才會被激活

   [subjectA sendCompleted];

   [subjectB sendNext:@"subjectB發(fā)送完信號”];

then

then:用于連接兩個信號躲叼,當?shù)谝粋€信號完成才會連接then返回的信號
使用then之前的信號會被忽略掉

merge

把多個信號合并為一個信號,任何一個信號有新值時就會調(diào)用

zipWith

把兩個信號壓縮成一個信號垄提,只有當兩個信號同時發(fā)出信號內(nèi)容的時候,并且把兩個信號的內(nèi)容合并成一個元組周拐,才會觸發(fā)壓縮流的next事件

reduce

聚合:用于信號發(fā)出的內(nèi)容是元組铡俐,把信號發(fā)出元組的值聚合成一個值。

fiter

使用提供的block來覺得事件是否往下傳遞

combineLatest:reduce

組合源信號數(shù)組中的信號妥粟,并生成一個新的信號审丘。每次源信號數(shù)組中的一個輸出新值時,reduce塊都會被執(zhí)行罕容,而返回的值會作為組合信號的下一個值备恤。

doNext

附加操作稿饰,并不返回一個值

參考文檔:http://www.reibang.com/p/87ef6720a096
http://blog.leichunfeng.com/blog/2015/12/25/reactivecocoa-v2-dot-5-yuan-ma-jie-xi-zhi-jia-gou-zong-lan/

Demo: https://github.com/jsonmall/reactcocoalearn.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市露泊,隨后出現(xiàn)的幾起案子喉镰,更是在濱河造成了極大的恐慌,老刑警劉巖惭笑,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侣姆,死亡現(xiàn)場離奇詭異,居然都是意外死亡沉噩,警方通過查閱死者的電腦和手機捺宗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來川蒙,“玉大人蚜厉,你說我怎么就攤上這事⌒笳#” “怎么了昼牛?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長康聂。 經(jīng)常有香客問我贰健,道長,這世上最難降的妖魔是什么恬汁? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任伶椿,我火速辦了婚禮,結(jié)果婚禮上氓侧,老公的妹妹穿的比我還像新娘脊另。我一直安慰自己,他們只是感情好甘苍,可當我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布尝蠕。 她就那樣靜靜地躺著,像睡著了一般载庭。 火紅的嫁衣襯著肌膚如雪看彼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天囚聚,我揣著相機與錄音靖榕,去河邊找鬼。 笑死顽铸,一個胖子當著我的面吹牛茁计,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谓松,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼星压,長吁一口氣:“原來是場噩夢啊……” “哼践剂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起娜膘,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤逊脯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后竣贪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體军洼,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年演怎,在試婚紗的時候發(fā)現(xiàn)自己被綠了匕争。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡爷耀,死狀恐怖甘桑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情畏纲,我是刑警寧澤扇住,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站盗胀,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏锄贼。R本人自食惡果不足惜票灰,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宅荤。 院中可真熱鬧屑迂,春花似錦、人聲如沸冯键。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惫确。三九已至手报,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間改化,已是汗流浹背掩蛤。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留陈肛,地道東北人揍鸟。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像句旱,于是被迫代替她去往敵國和親阳藻。 傳聞我的和親對象是個殘疾皇子晰奖,可洞房花燭夜當晚...
    茶點故事閱讀 45,037評論 2 355

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