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

一、簡介

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

聽周圍的人說,一旦你用熟練掌握了 (RAC)ReactiveCocoa,你就會慢慢依賴上它,懶得再用以前的方法了,因為它用起來實在太爽了。
于是最近我開始學(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)系。(abc 綁定起來了,所以 b、c 的變化會影響 a,這也就是所謂【變化傳播】)

2)函數(shù)式編程

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

  • 函數(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)是一個單純的運算過程,總是有返回值;”語句”(statement)是執(zhí)行某種操作,沒有返回值。函數(shù)式編程要求,只使用表達(dá)式,不使用語句。也就是說,每一步都是單純的運算,而且都有返回值。
    原因是函數(shù)式編程的開發(fā)動機(jī),一開始就是為了處理運算(computation),不考慮系統(tǒng)的讀寫(I/O)。”語句”屬于讀寫操作,所以就被排斥在外。
    函數(shù)式編程強(qiáng)調(diào)沒有”副作用”,意味著函數(shù)要保持獨立,所有功能就是返回一個新的值,沒有其他行為,尤其是不得修改外部變量的值。

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,項目中引入
<ReactiveObjC.h> 頭文件即可開始使用。

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

二、初級常用用法

1、通知 NSNotificationCenter

普通寫法:

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

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

2)實現(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中實現(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 代理為例

普通寫法:

需要實現(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:要實現(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 按鈕點擊事件

普通寫法:

1)為按鈕添加方法 addTarget

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

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

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

RAC 寫法:

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

下面是點擊按鈕后,控制臺打印出來的信息:

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時,會實時打印出變化后的文字

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é)參考了并部分摘抄了以下文章,非常感謝以下作者的分享?。?br> 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)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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