iOS開發ReactiveObjC總結

學習 RAC 我們首先要了解 RAC 都有哪些類

  • RACSignal  信號類
  • RACSubject  信號提供者,自己可以充當信號,又能發送信號.使用場景:通常用來代替代理,有了它,就不必要定義代理了。
  • RACSequence  信號的集合
  • RACMulticastConnection 用於當一個信號,被多次訂閱時,爲了保證創建信號時,避免多次調用創建信號中的block,造成副作用,可以使用這個類處理。
  • RACCommand  RAC中用於處理事件的類,可以把事件如何處理,事件中的數據如何傳遞,包裝到這個類中,他可以很方便的監控事件的執行過程。

RACSignal:

 RACSiganl:信號類,一般表示將來有數據傳遞,只要有數據改變,信號內部接收到數據,就會馬上發出數據。

 信號類(RACSiganl),只是表示當數據改變時,信號內部會發出數據,它本身不具備發送信號的能力,而是交給內部一個訂閱者去發出。

 默認一個信號都是冷信號,也就是值改變了,也不會觸發,只有訂閱了這個信號,這個信號纔會變爲熱信號,值改變了纔會觸發。

如何訂閱信號:調用信號RACSignal的subscribeNext就能訂閱。

//1.創建信號
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

//3.發送信號
[subscriber sendNext:@"發送信號"];
/**
如果不在發送數據,最好發送信號完成,內部會自動調用[RACDisposable disposable]取消訂閱信號
*/
[subscriber sendCompleted];

//取消訂閱方法
return [RACDisposable disposableWithBlock:^{
//block調用時刻:當信號發送完成或者發送錯誤,就會自動執行這個block,取消訂閱信號
// 執行完Block後,當前信號就不在被訂閱了。
NSLog(@"信號銷燬了");
}];
}];

//2.訂閱信號
[signal subscribeNext:^(id x) {
NSLog(@"訂閱信號:%@",x);
}];

 RACSignal底層實現

     1.創建信號,首先把didSubscribe保存到信號中,還不會觸發。

     2.當信號被訂閱,也就是調用signal的subscribeNext:nextBlock

     2.1 subscribeNext內部會調用siganl的didSubscribe

     2.2 subscribeNext內部會創建訂閱者subscriber,並且把nextBlock保存到subscriber中

     3.siganl的didSubscribe中調用[subscriber sendNext:@"發送信號"];

     3.1 sendNext底層其實就是執行subscriber的nextBlock

注意:

當一個信號被全局變量保存值的時候,我們要手動取消訂閱

RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

/**
有一個全局變量保存值就不會走下面取消訂閱方法
*/
_subscriber = subscriber;

[subscriber sendNext:@"123"];
[subscriber sendCompleted];

return [RACDisposable disposableWithBlock:^{
NSLog(@"走銷燬這個方法了");
}];
}];

 

RACDisposable 取消訂閱

//訂閱信號返回RACDisposable
RACDisposable *disposable = [signal1 subscribeNext:^(id x) {
NSLog(@"接收打印的值:%@",x);
}];

// 默認一個信號發送數據完畢們就會主動取消訂閱.
// 只要訂閱者在,就不會自動取消信號訂閱
// 手動取消訂閱者
[disposable dispose];

RACSubscriber:

表示訂閱者的意思,用於發送信號,這是一個協議,不是一個類,只要遵守這個協議,並且實現方法才能成爲訂閱者。通過create創建的信號,都有一個訂閱者,幫助他發送數據。

RACDisposable:用於取消訂閱或者清理資源,當信號發送完成或者發送錯誤的時候,就會自動觸發它。

     使用場景:不想監聽某個信號時,可以通過它主動取消訂閱信號。

     RACSubject:RACSubject:信號提供者,自己可以充當信號,又能發送信號。

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

     RACReplaySubject:重複提供信號類,RACSubject的子類。

    RACReplaySubject與RACSubject區別:

     RACReplaySubject可以先發送信號,在訂閱信號,RACSubject就不可以。

     使用場景一:如果一個信號每被訂閱一次,就需要把之前的值重複發送一遍,使用重複提供信號類。

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

     // RACSubject使用步驟

    // 1.創建信號 [RACSubject subject],跟RACSiganl不一樣,創建信號時沒有block。

    // 2.訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock

    // 3.發送信號 sendNext:(id)value

//1.創建信號
RACSubject *subject = [RACSubject subject];
//2.訂閱信號
[subject subscribeNext:^(id x) {
//當信號發出新值,就會調用.
NSLog(@"第一個訂閱者%@",x);
}];

[subject subscribeNext:^(id x) {
NSLog(@"第二個訂閱者%@",x);
}];

//3.發送信號
[subject sendNext:@"123456"];
//4.發送信號完成,內部會自動取消訂閱者
[subject sendCompleted];

//輸出:
// 第一個訂閱者123456
// 第二個訂閱者123456

RACReplaySubject

RACReplaySubject使用步驟:

1.創建信號 [RACSubject subject],跟RACSiganl不一樣,創建信號時沒有block。

2.可以先訂閱信號,也可以先發送信號。

2.1 訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock

2.2 發送信號 sendNext:(id)value 

RACReplaySubject:底層實現和RACSubject不一樣。

1.調用sendNext發送信號,把值保存起來,然後遍歷剛剛保存的所有訂閱者,一個一個調用訂閱者的nextBlock。

2.調用subscribeNext訂閱信號,遍歷保存的所有值,一個一個調用訂閱者的nextBlock

//1. RACReplaySubject *replaySubject = [RACReplaySubject subject];

//2.發送信號 [replaySubject sendNext:@"1"];

[replaySubject sendNext:@"2"];

//3.訂閱信號 [replaySubject subscribeNext:^(id x) {

NSLog(@"第一個訂閱者接收到的數據%@",x);

}];

[replaySubject subscribeNext:^(id x) {

NSLog(@"第二個訂閱者接收到的數據%@",x);

}];

//輸出:

//第一個訂閱者接收到的數據

1 //第一個訂閱者接收到的數據

2 //第二個訂閱者接收到的數據

1 //第二個訂閱者接收到的數據2

 

RAC集合

RACTuple:元組類,類似NSArray,用來包裝值. /**
RACTuple:元組類,類似NSArray,用來包裝值.
*/
//元組
RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:@[@"123",@"345",@1]];
NSString *first = tuple[0];
NSLog(@"%@",first);

//輸出:
//123

數組

NSArray *arr = @[@"213",@"321",@1];
//RAC 集合
// RACSequence *sequence = arr.rac_sequence;
// 把集合轉換成信號
// RACSignal *signal = sequence.signal;
// 訂閱集合信號,內部會自動遍歷所有的元素發出來
// [signal subscribeNext:^(id x) {
// NSLog(@"遍歷數組%@",x);
// }];
//輸出:
/**
遍歷數組213
遍歷數組321
遍歷數組1
*/

//高級寫法
[arr.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"高級寫法遍歷數組打印%@",x);
}];

//輸出:
/**
高級寫法遍歷數組打印213
高級寫法遍歷數組打印321
高級寫法遍歷數組打印1
*/

字典

NSDictionary *dict = @{@"sex":@"女",@"name":@"蒼老師",@"age":@18};

//轉換成集合
[dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
// NSString *key = x[0];
// NSString *value = x[1];
// NSLog(@"%@ %@",key,value);
// RACTupleUnpack:用來解析元組
// 宏裏面的參數,傳需要解析出來的變量名
//= 右邊,放需要解析的元組
RACTupleUnpack(NSString *key, NSString *value) = x;
NSLog(@"%@ %@",key,value);

}];
//輸出:
/**
sex 女
name 蒼老師
age 18
*/

字典轉模型NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];

// NSMutableArray *arr = [NSMutableArray array];
// // rac_sequence注意點:調用subscribeNext,並不會馬上執行nextBlock,而是會等一會。

// [dictArr.rac_sequence.signal subscribeNext:^(NSDictionary *x) {
// Flag *flag = [Flag flagWithDict:x];
// [arr addObject:flag];
// }];

//高級用法
// map:映射的意思,目的:把原始值value映射成一個新值
// array: 把集合轉換成數組
// 底層實現:當信號被訂閱,會遍歷集合中的原始值,映射成新值,並且保存到新的數組裏。
NSArray *arr = [[dictArr.rac_sequence map:^id(NSDictionary *value) {
return [Flag flagWithDict:value];
}] array];

NSLog(@"%@",arr);

RACMulticastConnection:

RACMulticastConnection:用於當一個信號,被多次訂閱時,爲了保證創建信號時,避免多次調用創建信號中的block,造成副作用,可以使用這個類處理。

 使用注意:RACMulticastConnection通過RACSignal的-publish或者-muticast:方法創建.

    RACMulticastConnection簡單使用:

     RACMulticastConnection使用步驟:

     1.創建信號 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe

     2.創建連接 RACMulticastConnection *connect = [signal publish];

     3.訂閱信號,注意:訂閱的不在是之前的信號,而是連接的信號。 [connect.signal subscribeNext:nextBlock]

     4.連接 [connect connect]

    

     RACMulticastConnection底層原理:

     1.創建connect,connect.sourceSignal -> RACSignal(原始信號)  connect.signal -> RACSubject

     2.訂閱connect.signal,會調用RACSubject的subscribeNext,創建訂閱者,而且把訂閱者保存起來,不會執行block。

     3.[connect connect]內部會訂閱RACSignal(原始信號),並且訂閱者是RACSubject

     3.1.訂閱原始信號,就會調用原始信號中的didSubscribe

     3.2 didSubscribe,拿到訂閱者調用sendNext,其實是調用RACSubject的sendNext

     4.RACSubject的sendNext,會遍歷RACSubject所有訂閱者發送信號。

     4.1 因爲剛剛第二步,都是在訂閱RACSubject,因此會拿到第二步所有的訂閱者,調用他們的nextBlock

     需求:假設在一個信號中發送請求,每次訂閱一次都會發送請求,這樣就會導致多次請求。

     解決:使用RACMulticastConnection就能解決.

//1.創建信號
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"發送請求");

[subscriber sendNext:@"1"];
return [RACDisposable disposableWithBlock:^{

}];
}];
//2.訂閱信號
[signal subscribeNext:^(id x) {

NSLog(@"接收數據");
}];
[signal subscribeNext:^(id x) {

NSLog(@"接收數據");
}];

// 3.運行結果,會執行兩遍發送請求,也就是每次訂閱都會發送一次請求

// RACMulticastConnection:解決重複請求問題
// 1.創建信號
RACSignal *connectionSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"發送請求--");
[subscriber sendNext:@1];

return [RACDisposable disposableWithBlock:^{

}];
}];

//2.創建連接
RACMulticastConnection *connect = [connectionSignal publish];

//3.訂閱信號
// 注意:訂閱信號,也不能激活信號,只是保存訂閱者到數組,必須通過連接,當調用連接,就會一次性調用所有訂閱者的sendNext:
[connect.signal subscribeNext:^(id x) {
NSLog(@"訂閱者一信號--");
}];
[connect.signal subscribeNext:^(id x) {

NSLog(@"訂閱者二信號--");

}];
//4.連接,激活信號
[connect connect];

//輸出:
/**
發送請求--
訂閱者一信號--
訂閱者二信號--
*/

RACCommand

RACCommand:RAC中用於處理事件的類,可以把事件如何處理,事件中的數據如何傳遞,包裝到這個類中,他可以很方便的監控事件的執行過程。

  使用場景:監聽按鈕點擊,網絡請求

一、RACCommand使用步驟:

1.創建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock

2.在signalBlock中,創建RACSignal,並且作爲signalBlock的返回值

3.執行命令 - (RACSignal *)execute:(id)input

二、RACCommand使用注意:

1.signalBlock必須要返回一個信號,不能傳nil.

2.如果不想要傳遞信號,直接創建空的信號[RACSignal empty];

3.RACCommand中信號如果數據傳遞完,必須調用[subscriber sendCompleted],這時命令纔會執行完畢,否則永遠處於執行中。

三、RACCommand設計思想:內部signalBlock爲什麼要返回一個信號,這個信號有什麼用。

1.在RAC開發中,通常會把網絡請求封裝到RACCommand,直接執行某個RACCommand就能發送請求。

2.當RACCommand內部請求到數據的時候,需要把請求的數據傳遞給外界,這時候就需要通過signalBlock返回的信號傳遞了。

四、如何拿到RACCommand中返回信號發出的數據。

1.RACCommand有個執行信號源executionSignals,這個是signal of signals(信號的信號),意思是信號發出的數據是信號,不是普通的類型。

2.訂閱executionSignals就能拿到RACCommand中返回的信號,然後訂閱signalBlock返回的信號,就能獲取發出的值。

五、監聽當前命令是否正在執行executing

六、使用場景,監聽按鈕點擊,網絡請求

//1.創建命令
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {

NSLog(@"執行命令");
//創建空信號,必須返回信號
//return [RACSignal empty];

//2.創建信號,用來傳遞數據
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

[subscriber sendNext:@"請求數據"];

//注意:數據傳遞完,最好調用sendCompleted,這時命令才執行完畢。
[subscriber sendCompleted];

return [RACDisposable disposableWithBlock:^{
NSLog(@"數據銷燬");
}];
}];
}];

//強引用命令,不要被銷燬,否則接收不到數據
_command = command;

//監聽事件有咩有完成
[command.executing subscribeNext:^(id x) {
if ([x boolValue] == YES) { // 當前正在執行
NSLog(@"當前正在執行");
}else{
// 執行完成/沒有執行
NSLog(@"執行完成/沒有執行");
}

}];

//執行命令
[self.command execute:@1];

RAC高級用法

//創建信號中信號
RACSubject *signalOfSignals = [RACSubject subject];
RACSubject *signalA = [RACSubject subject];
RACSubject *signalB = [RACSubject subject];
// 訂閱信號
// [signalOfSignals subscribeNext:^(RACSignal *x) {
// [x subscribeNext:^(id x) {
// NSLog(@"%@",x);
// }];
// }];
// switchToLatest:獲取信號中信號發送的最新信號
[signalOfSignals.switchToLatest subscribeNext:^(id x) {

NSLog(@"%@",x);
}];

// 發送信號
[signalOfSignals sendNext:signalA];

[signalA sendNext:@1];
[signalB sendNext:@"BB"];
[signalA sendNext:@"11"];

RACCommand:處理事件

RACCommand:不能返回一個空的信號

// 1.創建命令
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
// input:執行命令傳入參數
// Block調用:執行命令的時候就會調用
NSLog(@"%@",input);

return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

// 發送數據
[subscriber sendNext:@"執行命令產生的數據"];

return nil;
}];
}];

// 如何拿到執行命令中產生的數據
// 訂閱命令內部的信號
// 1.方式一:直接訂閱執行命令返回的信號
// 2.方式二:

// 2.執行命令
RACSignal *signal = [command execute:@1];

// 3.訂閱信號
[signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];

 

RAC 中常見的宏:

使用宏定義要單獨導入 #import <RACEXTScope.h>

 一. RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用於給某個對象的某個屬性綁定
只要文本框文字改變,就會修改label的文字
RAC(self.labelView,text) = _textField.rac_textSignal;

二.

RACObserve(self, name):監聽某個對象的某個屬性,返回的是信號。
[RACObserve(self.view, center) subscribeNext:^(id x) {
NSLog(@"%@",x);
}];

當RACObserve放在block裏面使用時一定要加上weakify,不管裏面有沒有使用到self;否則會內存泄漏,因爲RACObserve宏裏面就有一個self
@weakify(self);
RACSignal *signal3 = [anotherSignal flattenMap:^(NSArrayController *arrayController) {
Avoids a retain cycle because of RACObserve implicitly referencing self
@strongify(self);
return RACObserve(arrayController, items);
}];

 

三. @weakify(Obj)和@strongify(Obj),一般兩個都是配套使用,在主頭文件(ReactiveCocoa.h)中並沒有導入,需要自己手動導入,RACEXTScope.h纔可以使用。但是每次導入都非常麻煩,只需要在主頭文件自己導入就好了

四. RACTuplePack:把數據包裝成RACTuple(元組類)
把參數中的數據包裝成元組
RACTuple *tuple = RACTuplePack(@10,@20);

五.

RACTupleUnpack:把RACTuple(元組類)解包成對應的數據
// 把參數中的數據包裝成元組
RACTuple *tuple = RACTuplePack(@"xmg",@20);

// 解包元組,會把元組的值,按順序給參數裏面的變量賦值
// name = @"xmg" age = @20
RACTupleUnpack(NSString *name,NSNumber *age) = tuple;

 

RAC常用用法:

1.監聽按鈕的點擊事件:

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = CGRectMake(100, 100, 80, 40);
[button setTitle:@"點擊事件" forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor redColor]];
[self.view addSubview:button];

[[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"按鈕被點擊了");
}];

2.代理的使用:

@interface RedView : UIView

//*  */
@property (nonatomic, strong) RACSubject *btnClickSignal;

@end

#import "RedView.h"

@implementation RedView
- (void)awakeFromNib {

    [super awakeFromNib];
}

//懶加載信號
- (RACSubject *)btnClickSignal {

    if (_btnClickSignal == nil) {
        _btnClickSignal = [RACSubject subject];
    }
    return _btnClickSignal;
}

- (IBAction)btnClick:(UIButton *)sender {
    
    [self.btnClickSignal sendNext:@"按鈕被點擊了"];
}

@end

// 只要傳值,就必須使用RACSubject
RedView *redView = [[[NSBundle mainBundle] loadNibNamed:@"RedView" owner:nil options:nil] lastObject];
redView.frame = CGRectMake(0, 140, self.view.bounds.size.width, 200);
[self.view addSubview:redView];
[redView.btnClickSignal subscribeNext:^(id x) {
NSLog(@"---點擊按鈕了,觸發了事件");
}];

// 把控制器調用didReceiveMemoryWarning轉換成信號
//rac_signalForSelector:監聽某對象有沒有調用某方法

[[self rac_signalForSelector:@selector(didReceiveMemoryWarning)] subscribeNext:^(id x) {
NSLog(@"控制器調用didReceiveMemoryWarning");

}];

3.KVO

把監聽redV的center屬性改變轉換成信號,只要值改變就會發送信號

 // observer:可以傳入nil
    [[redView rac_valuesAndChangesForKeyPath:@"center" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
        NSLog(@"center:%@",x);
    }];

4.代替通知

UITextField *textfield = [[UITextField alloc] initWithFrame:CGRectMake(100, 340, 150, 40)];
textfield.placeholder = @"監聽鍵盤的彈起";
textfield.borderStyle = UITextBorderStyleRoundedRect;
[self.view addSubview:textfield];

[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
NSLog(@"監聽鍵盤的高度:%@",x);
}];

5.監聽文字的改變

1

2

3

4

[textfield.rac_textSignal subscribeNext:^(id x) {

     

     NSLog(@"輸入框中文字改變了---%@",x);

 }];

6.處理多個請求,都返回結果的時候,統一做處理.

RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

// 發送請求1
[subscriber sendNext:@"發送請求1"];
return nil;
}];

RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 發送請求2
[subscriber sendNext:@"發送請求2"];
return nil;
}];

// 使用注意:幾個信號,參數一的方法就幾個參數,每個參數對應信號發出的數據。
[self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[request1,request2]];


// 更新UI
- (void)updateUIWithR1:(id)data r2:(id)data1
{
NSLog(@"更新UI%@ %@",data,data1);
}

 

ReactiveObjC常見操作方法介紹:

1.1 v操作須知

 所有的信號(RACSignal)都可以進行操作處理,因爲所有操作方法都定義在RACStream.h中,而RACSignal繼承RACStream。

 1.2 ReactiveObjC操作思想

運用的是Hook(鉤子)思想,Hook是一種用於改變API(應用程序編程接口:方法)執行結果的技術.

 Hook用處:截獲API調用的技術。

 Hook原理:在每次調用一個API返回結果之前,先執行你自己的方法,改變結果的輸出。

 RAC開發方式:RAC中核心開發方式,也是綁定,之前的開發方式是賦值,而用RAC開發,應該把重心放在綁定,也就是可以在創建一個對象的時候,就綁定好以後想要做的事情,而不是等賦值之後在去做事情。

 列如:把數據展示到控件上,之前都是重寫控件的setModel方法,用RAC就可以在一開始創建控件的時候,就綁定好數據。

 1.3 ReactiveObjC核心方法bind

 ReactiveObjC操作的核心方法是bind(綁定),給RAC中的信號進行綁定,只要信號一發送數據,就能監聽到,從而把發送數據改成自己想要的數據。

在開發中很少使用bind方法,bind屬於RAC中的底層方法,RAC已經封裝了很多好用的其他方法,底層都是調用bind,用法比bind簡單.

 bind方法簡單介紹和使用。

// 假設想監聽文本框的內容,並且在每次輸出結果的時候,都在文本框的內容拼接一段文字“輸出:”

// 方式一:在返回結果後,拼接。
[_textField.rac_textSignal subscribeNext:^(id x) {

NSLog(@"輸出:%@",x);

}];

// 方式二:在返回結果前,拼接,使用RAC中bind方法做處理。
// bind方法參數:需要傳入一個返回值是RACStreamBindBlock的block參數
// RACStreamBindBlock是一個block的類型,返回值是信號,參數(value,stop),因此參數的block返回值也是一個block。

// RACStreamBindBlock:
// 參數一(value):表示接收到信號的原始值,還沒做處理
// 參數二(*stop):用來控制綁定Block,如果*stop = yes,那麼就會結束綁定。
// 返回值:信號,做好處理,在通過這個信號返回出去,一般使用RACReturnSignal,需要手動導入頭文件RACReturnSignal.h。

// bind方法使用步驟:
// 1.傳入一個返回值RACStreamBindBlock的block。
// 2.描述一個RACStreamBindBlock類型的bindBlock作爲block的返回值。
// 3.描述一個返回結果的信號,作爲bindBlock的返回值。
// 注意:在bindBlock中做信號結果的處理。

// 底層實現:
// 1.源信號調用bind,會重新創建一個綁定信號。
// 2.當綁定信號被訂閱,就會調用綁定信號中的didSubscribe,生成一個bindingBlock。
// 3.當源信號有內容發出,就會把內容傳遞到bindingBlock處理,調用bindingBlock(value,stop)
// 4.調用bindingBlock(value,stop),會返回一個內容處理完成的信號(RACReturnSignal)。
// 5.訂閱RACReturnSignal,就會拿到綁定信號的訂閱者,把處理完成的信號內容發送出來。

// 注意:不同訂閱者,保存不同的nextBlock,看源碼的時候,一定要看清楚訂閱者是哪個。
// 這裏需要手動導入#import <ReactiveCocoa/RACReturnSignal.h>,才能使用RACReturnSignal。

[[_textField.rac_textSignal bind:^RACStreamBindBlock{

// 什麼時候調用:
// block作用:表示綁定了一個信號.

return ^RACStream *(id value, BOOL *stop){

// 什麼時候調用block:當信號有新的值發出,就會來到這個block。

// block作用:做返回值的處理

// 做好處理,通過信號返回出去.
return [RACReturnSignal return:[NSString stringWithFormat:@"輸出:%@",value]];
};

}] subscribeNext:^(id x) {

NSLog(@"%@",x);

}];

flattenMap

     flattenMap使用步驟:

     1.傳入一個block,block類型是返回值RACStream,參數value

     2.參數value就是源信號的內容,拿到源信號的內容做處理

     3.包裝成RACReturnSignal信號,返回出去。

     

     flattenMap底層實現:

     0.flattenMap內部調用bind方法實現的,flattenMap中block的返回值,會作爲bind中bindBlock的返回值。

     1.當訂閱綁定信號,就會生成bindBlock。

     2.當源信號發送內容,就會調用bindBlock(value, *stop)

     3.調用bindBlock,內部就會調用flattenMap的block,flattenMap的block作用:就是把處理好的數據包裝成信號。

     4.返回的信號最終會作爲bindBlock中的返回信號,當做bindBlock的返回信號。

     5.訂閱bindBlock的返回信號,就會拿到綁定信號的訂閱者,把處理完成的信號內容發送出來。

[[_textField.rac_textSignal flattenMap:^RACStream *(id value) {
// block什麼時候 : 源信號發出的時候,就會調用這個block。

// block作用 : 改變源信號的內容。
// 返回值:綁定信號的內容.
return [RACReturnSignal return:[NSString stringWithFormat:@"輸出:%@",value]];
}] subscribeNext:^(id x) {
// 訂閱綁定信號,每當源信號發送內容,做完處理,就會調用這個block。
NSLog(@"輸出:flattenMap%@",x);
}];

 

map

     Map使用步驟:

     1.傳入一個block,類型是返回對象,參數是value

     2.value就是源信號的內容,直接拿到源信號的內容做處理

     3.把處理好的內容,直接返回就好了,不用包裝成信號,返回的值,就是映射的值。

 

     Map底層實現:

     0.Map底層其實是調用flatternMap,Map中block中的返回的值會作爲flatternMap中block中的值。

     1.當訂閱綁定信號,就會生成bindBlock。

     3.當源信號發送內容,就會調用bindBlock(value, *stop)

     4.調用bindBlock,內部就會調用flattenMap的block

     5.flattenMap的block內部會調用Map中的block,把Map中的block返回的內容包裝成返回的信號。

     5.返回的信號最終會作爲bindBlock中的返回信號,當做bindBlock的返回信號。

     6.訂閱bindBlock的返回信號,就會拿到綁定信號的訂閱者,把處理完成的信號內容發送出來。

[[_textField.rac_textSignal map:^id(id value) {
// 當源信號發出,就會調用這個block,修改源信號的內容
// 返回值:就是處理完源信號的內容。
return [NSString stringWithFormat:@"輸出:%@",value];
}] subscribeNext:^(id x) {

NSLog(@"map輸出%@",x);
}];

 

FlatternMap和Map的區別

  1.FlatternMap中的Block返回信號。

     2.Map中的Block返回對象。

     3.開發中,如果信號發出的值不是信號,映射一般使用Map

     4.開發中,如果信號發出的值是信號,映射一般使用FlatternMap。

     總結:signalOfsignals用FlatternMap。

//創建信號中的信號
RACSubject *signalOfsignals = [RACSubject subject];
RACSubject *signal = [RACSubject subject];

[[signalOfsignals flattenMap:^RACStream *(id value) {

//當signalOfsignals的signals發出信號纔會調用
return value;
}] subscribeNext:^(id x) {
// 只有signalOfsignals的signal發出信號纔會調用,因爲內部訂閱了bindBlock中返回的信號,也就是flattenMap返回的信號。
// 也就是flattenMap返回的信號發出內容,纔會調用。

NSLog(@"%@aaa",x);
}];

// 信號的信號發送信號
[signalOfsignals sendNext:signal];

// 信號發送內容
[signal sendNext:@1];

 

RAC操作之組合

1.concat

按一定順序拼接信號,當多個信號發出的時候,有順序的接收信號

//一.concat:按一定順序拼接信號,當多個信號發出的時候,有順序的接收信號。
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
[subscriber sendCompleted];
return nil;
}];

RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@2];
[subscriber sendCompleted];

return nil;
}];

// 把signalA拼接到signalB後,signalA發送完成,signalB纔會被激活。
RACSignal *concatSignal = [signalA concat:signalB];

// 以後只需要面對拼接信號開發。
// 訂閱拼接的信號,不需要單獨訂閱signalA,signalB
// 內部會自動訂閱。
// 注意:第一個信號必須發送完成,第二個信號纔會被激活
[concatSignal subscribeNext:^(id x) {

NSLog(@"%@",x);
}];

// concat底層實現:
// 1.當拼接信號被訂閱,就會調用拼接信號的didSubscribe
// 2.didSubscribe中,會先訂閱第一個源信號(signalA)
// 3.會執行第一個源信號(signalA)的didSubscribe
// 4.第一個源信號(signalA)didSubscribe中發送值,就會調用第一個源信號(signalA)訂閱者的nextBlock,通過拼接信號的訂閱者把值發送出來.
// 5.第一個源信號(signalA)didSubscribe中發送完成,就會調用第一個源信號(signalA)訂閱者的completedBlock,訂閱第二個源信號(signalB)這時候才激活(signalB)。
// 6.訂閱第二個源信號(signalB),執行第二個源信號(signalB)的didSubscribe
// 7.第二個源信號(signalA)didSubscribe中發送值,就會通過拼接信號的訂閱者把值發送出來.

 

2.then

用於連接兩個信號,當第一個信號完成,纔會連接then返回的信號

then:用於連接兩個信號,當第一個信號完成,纔會連接then返回的信號

注意使用then,之前信號的值會被忽略掉.

底層實現:1、先過濾掉之前的信號發出的值。2.使用concat連接then返回的信號

[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

[subscriber sendNext:@1];
[subscriber sendCompleted];

return nil;

}] then:^RACSignal *{
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@2];
return nil;
}];
}] subscribeNext:^(id x) {
// 只能接收到第二個信號的值,也就是then返回信號的值
NSLog(@"%@",x);
}];

 

3merge

把多個信號合併爲一個信號,任何一個信號有新值的時候就會調用.

    merge: 把多個信號合併成一個信號

    創建多個信號 RACSignal *signalC = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

[subscriber sendNext:@3];

return [RACDisposable disposableWithBlock:^{
NSLog(@"信號釋放了");
}];
}];

RACSignal *signalD = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@4];

return [RACDisposable disposableWithBlock:^{
NSLog(@"信號釋放了");
}];
}];

//合併信號.任何一個信號發送數據,都能監聽到

RACSignal *mergeSignal = [signalC merge:signalD];
[mergeSignal subscribeNext:^(id x) {
NSLog(@"合併信號,任何一個數據發送數據,都能監聽到%@",x);
}];

// 底層實現:
// 1.合併信號被訂閱的時候,就會遍歷所有信號,並且發出這些信號。
// 2.每發出一個信號,這個信號就會被訂閱
// 3.也就是合併信號一被訂閱,就會訂閱裏面所有的信號。
// 4.只要有一個信號被髮出就會被監聽。

 

4.zipWith

把兩個信號壓縮成一個信號,只有當兩個信號同時發出信號內容時,並且把兩個信號的內容合併成一個元組,纔會觸發壓縮流的next事件。

RACSignal *signalE = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@5];

return [RACDisposable disposableWithBlock:^{

}];
}];

RACSignal *signalF = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@6];

return [RACDisposable disposableWithBlock:^{

}];
}];

// 壓縮信號A,信號B

RACSignal *zipSignal = [signalE zipWith:signalF];

[zipSignal subscribeNext:^(id x) {
NSLog(@"壓縮的信號%@",x);
}];

// 底層實現:
// 1.定義壓縮信號,內部就會自動訂閱signalA,signalB
// 2.每當signalA或者signalB發出信號,就會判斷signalA,signalB有沒有發出個信號,有就會把最近發出的信號都包裝成元組發出。

 

5.combineLatest

將多個信號合併起來,並且拿到各個信號的最新的值,必須每個合併的signal至少都有過一次sendNext,纔會觸發合併的信號.

RACSignal *signalG = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@5];

return [RACDisposable disposableWithBlock:^{

}];
}];

RACSignal *signalH = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@6];

return [RACDisposable disposableWithBlock:^{

}];
}];

// 把兩個信號組合成一個信號,跟zip一樣,沒什麼區別
RACSignal *combineSignal = [signalG combineLatestWith:signalH];

[combineSignal subscribeNext:^(id x) {
NSLog(@"把兩個信號組合成一個信號%@",x);
}];

// 底層實現:
// 1.當組合信號被訂閱,內部會自動訂閱signalA,signalB,必須兩個信號都發出內容,纔會被觸發。
// 2.並且把兩個信號組合成元組發出。

 

6.reduce聚合

用於信號發出的內容是元組,把信號發出元組的值聚合成一個值

RACSignal *signalL = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@5];

return [RACDisposable disposableWithBlock:^{

}];
}];

RACSignal *signalM = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@6];

return [RACDisposable disposableWithBlock:^{

}];
}];

RACSignal *reduceSignal = [RACSignal combineLatest:@[signalL,signalM] reduce:^id(NSNumber *num1, NSNumber *num2){
return [NSString stringWithFormat:@"reduce聚合%@ %@",num1,num2];
}];

[reduceSignal subscribeNext:^(id x) {
NSLog(@"%@",x);

}];

// // 底層實現:
// 1.訂閱聚合信號,每次有內容發出,就會執行reduceblcok,把信號內容轉換成reduceblcok返回的值。

ReactiveCocoa常見操作方法介紹:

  • filter
  • ignore
  • ignoreValues
  • takeUntilBlock
  • distinctUntilChanged
  • take
  • takeLast
  • takeUntil
  • skip
  • switchToLatest

filter: 過濾信號,使用它可以獲取滿足條件的信號.

//filter 過濾
    //每次信號發出,會先執行過濾條件判斷
    [_textField.rac_textSignal filter:^BOOL(NSString *value) {
       
        return value.length > 3;
    }];

ignore:忽略完某些值的信號.

// 內部調用filter過濾,忽略掉ignore的值
    [[_textField.rac_textSignal ignore:@"1"] subscribeNext:^(id x) {
        NSLog(@"ignore%@",x);
    }];

ignoreValues 這個比較極端,忽略所有值,只關心Signal結束,也就是隻取Comletion和Error兩個消息,中間所有值都丟棄

RACSignal *signal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1"];
[subscriber sendNext:@"3"];
[subscriber sendNext:@"15"];
[subscriber sendNext:@"wujy"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"執行清理");
}];
}];


[[signal ignoreValues] subscribeNext:^(id x) {
//它是沒機會執行 因爲ignoreValues已經忽略所有的next值
NSLog(@"ignoreValues當前值:%@",x);
} error:^(NSError *error) {
NSLog(@"ignoreValues error");
} completed:^{
NSLog(@"ignoreValues completed");
}];

 

distinctUntilChanged:當上一次的值和當前的值有明顯的變化就會發出信號,否則會被忽略掉。 // 過濾,當上一次和當前的值不一樣,就會發出內容。
// 在開發中,刷新UI經常使用,只有兩次數據不一樣才需要刷新

[[_textField.rac_textSignal distinctUntilChanged] subscribeNext:^(id x) {
NSLog(@"distinctUntilChanged%@",x);
}];

take:從開始一共取N次的信號

//1.創建信號
RACSubject *signal = [RACSubject subject];
//2.處理信號,訂閱信號
[[signal take:1] subscribeNext:^(id x) {
NSLog(@"take:%@",x);

}];

// 3.發送信號
[signal sendNext:@1];

[signal sendNext:@2];

 

takeLast:取最後N次的信號,前提條件,訂閱者必須調用完成,因爲只有完成,就知道總共有多少信號.

//1.創建信號
RACSubject *signal = [RACSubject subject];
//2.處理信號,訂閱信號
[[signal takeLast:2] subscribeNext:^(id x) {
NSLog(@"%@",x);

}];

//3.發送信號
[signal sendNext:@1];
[signal sendNext:@332];
[signal sendNext:@333];
[signal sendCompleted];

 

takeUntil:(RACSignal *):獲取信號直到執行完這個信號 // 監聽文本框的改變,知道當前對象被銷燬
[_textField.rac_textSignal takeUntil:self.rac_willDeallocSignal];

takeUntilBlock 對於每個next值,運行block,當block返回YES時停止取值

RACSignal *signal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1"];
[subscriber sendNext:@"3"];
[subscriber sendNext:@"15"];
[subscriber sendNext:@"wujy"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"執行清理");
}];
}];

[[signal takeUntilBlock:^BOOL(NSString *x) {
if ([x isEqualToString:@"15"]) {
return YES;
}
return NO;
}] subscribeNext:^(id x) {
NSLog(@"takeUntilBlock 獲取的值:%@",x);
}];

// 輸出
// takeUntilBlock 獲取的值:1
// takeUntilBlock 獲取的值:3

 

skip:(NSUInteger):跳過幾個信號,不接受。

 [[_textField.rac_textSignal skip:1] subscribeNext:^(id x) {
        NSLog(@"跳過幾個信號不接收%@",x);
    }];

switchToLatest:用於signalOfSignals(信號的信號),有時候信號也會發出信號,會在signalOfSignals中,獲取signalOfSignals發送的最新信號。

RACSubject *signalOfSignals = [RACSubject subject];
RACSubject *signal = [RACSubject subject];
[signalOfSignals sendNext:signal];
// 獲取信號中信號最近發出信號,訂閱最近發出的信號。
// 注意switchToLatest:只能用於信號中的信號

[signalOfSignals.switchToLatest subscribeNext:^(id x) {

NSLog(@"獲取信號中信號最近發出信號,訂閱最近發出的信號%@",x);
}];

 

RAC操作方法三.

  • doNext
  • deliverOn
  • timeout
  • interval
  • delay
  • retry
  • replay
  • throttle

//ReactiveCocoa操作方法之秩序。

- (void)doNext {

//doNext: 執行Next之前,會先執行這個Block
//doCompleted: 執行sendCompleted之前,會先執行這個Block
[[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
[subscriber sendCompleted];
return nil;
}] doNext:^(id x) {
// 執行[subscriber sendNext:@1];之前會調用這個Block
NSLog(@"doNext");
}] doCompleted:^{
// 執行[subscriber sendCompleted];之前會調用這個Block
NSLog(@"doCompleted");
}] subscribeNext:^(id x) {
NSLog(@"%@",x);

}];
}

 

//ReactiveCocoa操作方法之線程。

- (void)deliverOn {
    //deliverOn: 內容傳遞切換到制定線程中,副作用在原來線程中,把在創建信號時block中的代碼稱之爲副作用。
    
    //subscribeOn: 內容傳遞和副作用都會切換到制定線程中。
}

//ReactiveCocoa操作方法之時間。

- (void)timeout {
//timeout:超時,可以讓一個信號在一定的時間後,自動報錯。
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
return nil;
}] timeout:1 onScheduler:[RACScheduler currentScheduler]];

[signal subscribeNext:^(id x) {

NSLog(@"%@",x);

} error:^(NSError *error) {
// 1秒後會自動調用
NSLog(@"%@",error);
}];
}

 

//interval 定時:每隔一段時間發出信號

- (void)interval {

[[RACSignal interval:1 onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(id x) {
NSLog(@"%@",x);
} error:^(NSError *error) {
NSLog(@"%@",error);
}];
}

 

//delay 延遲發送next

- (void)delay {

[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

[subscriber sendNext:@1];
return nil;
}] delay:2] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}

 

// ReactiveObjC操作方法之重複。

- (void)retry {
//retry重試 :只要失敗,就會重新執行創建信號中的block,直到成功.
__block int i = 0;
[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

if (i == 10) {
[subscriber sendNext:@1];
}else{
NSLog(@"接收到錯誤");
[subscriber sendError:nil];
}
i++;
return nil;

}] retry] subscribeNext:^(id x) {

NSLog(@"%@",x);

} error:^(NSError *error) {


}];
}

 

//replay重放:當一個信號被多次訂閱,反覆播放內容

- (void)replay {
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {


[subscriber sendNext:@1];
[subscriber sendNext:@2];

return nil;
}] replay];

[signal subscribeNext:^(id x) {

NSLog(@"第一個訂閱者%@",x);

}];

[signal subscribeNext:^(id x) {

NSLog(@"第二個訂閱者%@",x);

}];
}

 

throttle

- (void)throttle {
RACSubject *signal = [RACSubject subject];
_signal = signal;
// 節流,在一定時間(1秒)內,不接收任何信號內容,過了這個時間(1秒)獲取最後發送的信號內容發出。
[[signal throttle:1] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}

 

demo地址:https://github.com/13662049573/TFY_ReactiveCocoaDemo.git

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章