RAC(ReactiveCocoa) 學(xué)習(xí)--初探

一死陆、簡介

1对嚼、 函數(shù)響應(yīng)式編程 FRP(Functional Reactive Programming)

聽周圍的人說司蔬,一旦你用熟練掌握了 (RAC)ReactiveCocoa域携,你就會慢慢依賴上它巴帮,懶得再用以前的方法了,因?yàn)樗闷饋韺?shí)在太爽了瞬女。
于是最近我開始學(xué)習(xí) RAC 框架凤覆,從而了解到了函數(shù)響應(yīng)式編程這一概念。RAC 的核心思想就是函數(shù)式 + 響應(yīng)式編程拆魏。 據(jù)說 FRP 能讓你的代碼像數(shù)學(xué)一樣簡潔盯桦,業(yè)務(wù)像流水一樣清晰流暢。
RAC 屬于 Rx 大家族之一渤刃,除了 RAC拥峦,在 Swift 方面還有 RxSwiftRxSwift 可以看我的另一篇文章《RxSwift 學(xué)習(xí)(一)—— 初探》卖子。

1)響應(yīng)式編程

例如略号,在命令式編程環(huán)境中,a = b + c 表示將表達(dá)式的結(jié)果賦給 a洋闽,而之后改變 bc 的值不會影響 a玄柠。但在響應(yīng)式編程中,a 的值會隨著 bc 的更新而更新诫舅。
在響應(yīng)式編程當(dāng)中羽利,a = b + c聲明的是一種綁定關(guān)系。(ab刊懈、c 綁定起來了这弧,所以 bc 的變化會影響 a虚汛,這也就是所謂【變化傳播】)

2)函數(shù)式編程

函數(shù)式編程具有以下幾個特點(diǎn):

  • 函數(shù)是”第一等公民”
    所謂”第一等公民”(first class)匾浪,指的是函數(shù)與其他數(shù)據(jù)類型一樣,處于平等地位卷哩,可以賦值給其他變量蛋辈,也可以作為參數(shù),傳入另一個函數(shù)将谊,或者作為別的函數(shù)的返回值冷溶。
  • 閉包和高階函數(shù)
    函數(shù)式編程抽取了很多常用操作,作為高階函數(shù)瓢娜,比如map挂洛,filter礼预,reduce眠砾。 有了這些函數(shù),你的代碼將被大大簡化,也意味著你可以進(jìn)行更加快速的開發(fā)褒颈,同時這些函數(shù)也幫助別人理解你的代碼柒巫。
  • 不改變狀態(tài)
    不依賴于外部的數(shù)據(jù),而且也不改變外部數(shù)據(jù)的值谷丸,而是返回一個新的值給你堡掏。
  • 遞歸
    函數(shù)式編程是用遞歸做為控制流程的機(jī)制
  • 只用“表達(dá)式”,不用“語句”刨疼,沒有副作用
    “表達(dá)式”(expression)是一個單純的運(yùn)算過程泉唁,總是有返回值;”語句”(statement)是執(zhí)行某種操作揩慕,沒有返回值亭畜。函數(shù)式編程要求,只使用表達(dá)式迎卤,不使用語句拴鸵。也就是說,每一步都是單純的運(yùn)算蜗搔,而且都有返回值劲藐。
    原因是函數(shù)式編程的開發(fā)動機(jī),一開始就是為了處理運(yùn)算(computation)樟凄,不考慮系統(tǒng)的讀寫(I/O)聘芜。”語句”屬于讀寫操作缝龄,所以就被排斥在外厉膀。
    函數(shù)式編程強(qiáng)調(diào)沒有”副作用”,意味著函數(shù)要保持獨(dú)立二拐,所有功能就是返回一個新的值服鹅,沒有其他行為,尤其是不得修改外部變量的值百新。

2企软、 subscribeNext 函數(shù)

RAC 有一個強(qiáng)大的訂閱函數(shù) subscribeNext

- (RACDisposable *)subscribeNext:(void (^)(ValueType _Nullable x))nextBlock;

這個函數(shù)可以為任何 RACSignal信號 類型的對象進(jìn)行訂閱,用 block 進(jìn)行回調(diào)饭望。RAC為很多類做了分類仗哨,都有返回 RACSignal信號類型,這種方式使得平時很多麻煩的操作都變簡潔了铅辞,少寫了很多代碼厌漂。

3、RAC的導(dǎo)入

我使用的是 cocoaPods 導(dǎo)入的斟珊,使用的是3.0.0版本苇倡,在 Podfile 文件里寫入pod 'ReactiveObjC', '~> 3.0.0',再在命令行中 pod install,項(xiàng)目中引入
<ReactiveObjC.h> 頭文件即可開始使用旨椒。

下面舉幾個常用的例子晓褪,分別用普通寫法和 RAC 的寫法進(jìn)行對比,來見證一下 RAC 的強(qiáng)大综慎。

二涣仿、初級常用用法

1、通知 NSNotificationCenter

普通寫法:

1)添加鍵盤彈出的通知addObserver

[[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];

2)實(shí)現(xiàn)通知 keyboardWillShow

- (void) keyboardWillShow:(NSNotification *)note {
    NSLog(@"鍵盤彈出了");
}

3)在析構(gòu)函數(shù)中移除通知removeObserver

[[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];

RAC 寫法:

[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
    NSLog(@"%@",x);
}];

2示惊、KVO

例如好港,當(dāng)KVO監(jiān)聽name屬性變化

普通寫法:

1)添加監(jiān)聽addObserver

[self.textField addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

2)在方法 observeValueForKeyPath中實(shí)現(xiàn)監(jiān)聽到屬性值變化后的處理

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([@"text" isEqualToString: keyPath] && object == self.textField) {
        NSLog(@"%@", change);
    }
}

3)在析構(gòu)函數(shù)中移除監(jiān)聽removeObserver

[self.textField removeObserver:self forKeyPath:@"text"];

RAC 寫法:

RAC 中的 KVO 大部分都是宏定義,所以代碼異常簡潔米罚,簡單來說就是RACObserve(TARGET, KEYPATH)這種形式媚狰,TARGET 是監(jiān)聽目標(biāo),KEYPATH 是要觀察的屬性值阔拳。

[RACObserve(self.textField, text) subscribeNext:^(id  _Nullable x) {
    NSLog(@"%@",x);
}];
self.textField.text = @"凡幾多";

3崭孤、代理 delegate

UITextFieldtextFieldDidBeginEditing 代理為例

普通寫法:

需要實(shí)現(xiàn)代理方法,增加了代碼量糊肠,看起來也不方便辨宠。

- (void)textFieldDidBeginEditing:(UITextField *)textField {
    NSLog(@"開始編輯");
}

RAC 寫法:

[[self rac_signalForSelector:@selector(textFieldDidBeginEditing:) fromProtocol:@protocol(UITextFieldDelegate)] subscribeNext:^(RACTuple * _Nullable x) {
    NSLog(@"%@",x);
}];
self.textField.delegate = self;
  • - (RACSignal<RACTuple *> *)rac_signalForSelector:(SEL)selector fromProtocol:(Protocol *)protocol;
    方法選擇器參數(shù)@selector:要實(shí)現(xiàn)的具體代理方法
    代理名稱參數(shù)fromProtocol:對應(yīng)的代理名稱。

下面是在開始編輯self.textField后货裹,控制臺打印出來的信息:

2019-05-16 17:02:07.753157+0800 001---RAC
[54156:4473541] <RACTuple: 0x600002b706b0> (
    "<UITextField: 0x7f84fc013e00; frame = (141 128; 97 30); text = ''; opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x60000272b210>; layer = <CALayer: 0x6000029616c0>>"
)

觸發(fā)代理方法后嗤形,block 回調(diào)返回的是元組類型數(shù)據(jù)。

4弧圆、UIButton 按鈕點(diǎn)擊事件

普通寫法:

1)為按鈕添加方法 addTarget

[self.button addTarget:self action:@selector(onBtnClick:) forControlEvents:UIControlEventTouchUpInside];

2)實(shí)現(xiàn)按鈕的點(diǎn)擊方法onBtnClick

- (void)onBtnClick:(UIButton *)sender {
    NSLog(@"點(diǎn)擊按鈕了");
}

RAC 寫法:

[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
    NSLog(@"%@",x);
}];

下面是點(diǎn)擊按鈕后赋兵,控制臺打印出來的信息:

2019-05-16 17:08:43.841912+0800 001---RAC[54247:4481723]
 <UIButton: 0x7fb967d118e0; frame = (166 223; 46 256); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x600000942880>>

5、UITextField

RAC 寫法:

[self.textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
    NSLog(@"%@",x);
}];

當(dāng)在self.textField輸入文字h時搔预,會實(shí)時打印出變化后的文字

2019-05-16 18:02:58.562309+0800 001---RAC[54530:4536864] h
2019-05-16 18:02:59.049225+0800 001---RAC[54530:4536864] hh
2019-05-16 18:02:59.288995+0800 001---RAC[54530:4536864] hhh

6霹期、手勢 UITapGestureRecognizer

RAC 寫法:

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
self.label.userInteractionEnabled = YES;
[self.label addGestureRecognizer:tap];
[tap.rac_gestureSignal subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
    NSLog(@"%@",x);
}];

7、數(shù)組和字典的遍歷

普通寫法:

普通寫法遍歷數(shù)組和字典都是需要寫一個 for 循環(huán)進(jìn)行遍歷拯田。

RAC 寫法:

1)數(shù)組:

NSArray *array = @[@"凡幾多",@"感",@"最瀟灑"];
[array.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
    NSLog(@"%@",x);
}];

控制臺打印出來的數(shù)組信息

2019-05-16 17:14:40.546191+0800 001---RAC[54329:4488306] 凡幾多
2019-05-16 17:14:40.546569+0800 001---RAC[54329:4488306] 感
2019-05-16 17:14:40.546761+0800 001---RAC[54329:4488306] 最瀟灑

2)字典:

NSDictionary *dict = @{@"name":@"凡幾多",@"age":@"20",@"sex":@"男"};
[dict.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
    //元祖
    NSLog(@"%@",x);
    RACTwoTuple *tuple = (RACTwoTuple *)x;
    NSLog(@"key == %@ , value = %@",tuple[0],tuple[1]);
}];

控制臺打印出來的數(shù)組信息

2019-05-16 17:20:02.538642+0800 001---RAC[54365:4494005] 
<RACTwoTuple: 0x600000c745e0> (
    name,
    "\U51e1\U51e0\U591a"
)
2019-05-16 17:20:02.539301+0800 001---RAC[54365:4494005] 
key == name , value = 凡幾多
2019-05-16 17:20:02.540359+0800 001---RAC[54365:4494005] 
<RACTwoTuple: 0x600000c7c580> (
    age,
    20
)
2019-05-16 17:20:02.540577+0800 001---RAC[54365:4494005] 
key == age , value = 20
2019-05-16 17:20:02.542622+0800 001---RAC[54365:4494005] 
<RACTwoTuple: 0x600000c74660> (
    sex,
    "\U7537"
)
2019-05-16 17:20:02.542774+0800 001---RAC[54365:4494005] 
key == sex , value = 男

三历造、RAC最基本的用法流程

創(chuàng)建信號、訂閱信號船庇、發(fā)送信號

//1:創(chuàng)建信號
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    //subscriber 對象不是一個對象
    //3:發(fā)送信號
    [subscriber sendNext:@"Cooci"];
        
    //請求網(wǎng)絡(luò) 失敗 error
    NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:10086 userInfo:@{@"key":@"10086錯誤"}];
    [subscriber sendError:error];

    // RACDisposable 銷毀
    return [RACDisposable disposableWithBlock:^{
        NSLog(@"銷毀了");
    }];
}];

//2:訂閱信號
[signal subscribeNext:^(id  _Nullable x) {
    NSLog(@"%@",x);
}];
//訂閱錯誤信號
[signal subscribeError:^(NSError * _Nullable error) {
    NSLog(@"%@",error);
}];

以上的總結(jié)參考了并部分摘抄了以下文章吭产,非常感謝以下作者的分享!:
1鸭轮、作者zzfx的《函數(shù)響應(yīng)式編程(FRP)從入門到”放棄”——基礎(chǔ)概念篇》
2臣淤、作者Philm_iOS的《函數(shù)響應(yīng)式編程》
3、作者我只不過是出來寫寫代碼的《RAC(ReactiveCocoa)介紹(一)——基本介紹》

轉(zhuǎn)載請備注原文出處窃爷,不得用于商業(yè)傳播——凡幾多

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邑蒋,一起剝皮案震驚了整個濱河市姓蜂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寺董,老刑警劉巖覆糟,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刻剥,死亡現(xiàn)場離奇詭異遮咖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)造虏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進(jìn)店門御吞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人漓藕,你說我怎么就攤上這事陶珠。” “怎么了享钞?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵揍诽,是天一觀的道長。 經(jīng)常有香客問我栗竖,道長暑脆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任狐肢,我火速辦了婚禮添吗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘份名。我一直安慰自己碟联,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布僵腺。 她就那樣靜靜地躺著鲤孵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪辰如。 梳的紋絲不亂的頭發(fā)上裤纹,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機(jī)與錄音丧没,去河邊找鬼鹰椒。 笑死,一個胖子當(dāng)著我的面吹牛呕童,可吹牛的內(nèi)容都是我干的漆际。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼夺饲,長吁一口氣:“原來是場噩夢啊……” “哼奸汇!你這毒婦竟也來了施符?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤擂找,失蹤者是張志新(化名)和其女友劉穎戳吝,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贯涎,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡听哭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了塘雳。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陆盘。...
    茶點(diǎn)故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖败明,靈堂內(nèi)的尸體忽然破棺而出隘马,到底是詐尸還是另有隱情,我是刑警寧澤妻顶,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布酸员,位于F島的核電站,受9級特大地震影響讳嘱,放射性物質(zhì)發(fā)生泄漏幔嗦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一呢燥、第九天 我趴在偏房一處隱蔽的房頂上張望崭添。 院中可真熱鬧,春花似錦叛氨、人聲如沸呼渣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽屁置。三九已至,卻和暖如春仁连,著一層夾襖步出監(jiān)牢的瞬間蓝角,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工饭冬, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留使鹅,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓昌抠,卻偏偏與公主長得像患朱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子炊苫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,960評論 2 355

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