一死陆、簡介
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 方面還有 RxSwift,RxSwift 可以看我的另一篇文章《RxSwift 學(xué)習(xí)(一)—— 初探》卖子。
1)響應(yīng)式編程
例如略号,在命令式編程環(huán)境中,a = b + c 表示將表達(dá)式的結(jié)果賦給 a洋闽,而之后改變 b 或 c 的值不會影響 a玄柠。但在響應(yīng)式編程中,a 的值會隨著 b 或 c 的更新而更新诫舅。
在響應(yīng)式編程當(dāng)中羽利,a = b + c聲明的是一種綁定關(guān)系。(a 與 b刊懈、c 綁定起來了这弧,所以 b、c 的變化會影響 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
以 UITextField 的 textFieldDidBeginEditing
代理為例
普通寫法:
需要實(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è)傳播——凡幾多