RAC 使用方法總結

alloc發表於2017-12-19

RACSignal

/**

*
一.核心:
1.核心:訊號類
2.訊號類的作用:只要有資料改變就會把資料包裝成訊號傳遞出去
3.只要有資料改變就會有訊號發出
4.資料發出,並不是訊號類發出,訊號類不能傳送資料
一.使用方法:
1.建立訊號
2.訂閱訊號
二.實現思路:
1.當一個訊號被訂閱,建立訂閱者,並把nextBlock儲存到訂閱者裡面。
2.建立的時候會返回 [RACDynamicSignal createSignal:didSubscribe];
3.呼叫RACDynamicSignal的didSubscribe
4.傳送訊號[subscriber sendNext:value];
5.拿到訂閱者的nextBlock呼叫
*/

// 1.建立訊號
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3.傳送訊號
[subscriber sendNext:@"ws"];
// 4.取消訊號,如果訊號想要被取消,就必須返回一個RACDisposable
// 訊號什麼時候被取消:1.自動取消,當一個訊號的訂閱者被銷燬的時候機會自動取消訂閱,2.手動取消,
//block什麼時候呼叫:一旦一個訊號被取消訂閱就會呼叫
//block作用:當訊號被取消時用於清空一些資源
return [RACDisposable disposableWithBlock:^{
NSLog(@"取消訂閱");
}];
}];
// 2. 訂閱訊號
//subscribeNext
// 把nextBlock儲存到訂閱者裡面
// 只要訂閱訊號就會返回一個取消訂閱訊號的類
RACDisposable *disposable = [signal subscribeNext:^(id x) {
// block的呼叫時刻:只要訊號內部發出資料就會呼叫這個block
NSLog(@"======%@", x);
}];
// 取消訂閱
[disposable dispose];

}


RACSignal使用步驟

/**
*1.建立訊號
*2.訂閱訊號(訂閱訊號後,才會被啟用)
*3.傳送訊號

/RACSignal底層實現
1、建立訊號,首先把didSubscribe儲存到訊號中,還不會觸發
2、當訊號被訂閱,也就是呼叫signal的subscribrNext:nextBlock, subscribeNext內部會建立訂閱者subscriber,並把nextBlock儲存到subscriber中,subscribNext內部會呼叫signal的didSubscribe
3、signal的didsubcribe中呼叫【subscriber sendNext:@"傳送資料"】,sendNext底層其實就是執行subscriber的nextBlock。
*/

 //1.建立訊號
RACSignal *mysignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
//2.傳送訊號
[subscriber sendNext:@"傳送資料"];

[subscriber sendCompleted];//如果不再傳送資料,需要呼叫完成方法,內部會呼叫 [self.disposable dispose];

return [RACDisposable disposableWithBlock:^{
NSLog(@"訊號被銷燬");
}];
}];
//3.訂閱訊號,才會啟用訊號
[mysignal subscribeNext:^(id x) {
NSLog(@"接受到資料:%@",x);
}];


RACSubject

/**
*我們完全可以用RACSubject代替代理/通知,確實方便許多
步驟:
// 1.建立訊號
RACSubject *subject = [RACSubject subject];

// 2.訂閱訊號
[subject subscribeNext:^(id x) {
// block:當有資料發出的時候就會呼叫
// block:處理資料
NSLog(@"%@",x);
}];

// 3.傳送訊號
[subject sendNext:value];
**注意:~~**
RACSubject和RACReplaySubject的區別
RACSubject必須要先訂閱訊號之後才能傳送訊號, 而RACReplaySubject可以先傳送訊號後訂閱.
可按實際情況各取所需。

*/

RACSubject使用步驟

/**
1、建立訊號 【RACSubject subject】,跟RACSignal不一樣,建立訊號時沒有block。
2、訂閱訊號 -(RACDisposable *)subscribeNext:(void(^)(id x)nextBlock)
3、傳送訊號 sengNext:(id Value)

RACSubject : 底層實現跟RACSignal不一樣
1、呼叫subscribeNext訂閱訊號,只是把訂閱者儲存起來,並且訂閱者的nextBlock已經賦值了。
2、呼叫sendNext傳送訊號,遍歷剛剛儲存的所有訂閱者,一個一個呼叫訂閱者的nextBlock
*/

//1、建立訊號
RACSubject *subject = [RACSubject subject];

//2、訂閱訊號
[subject subscribeNext:^(id x) {
//block呼叫時刻:當訊號發出新值,就會呼叫
NSLog(@"第一個訂閱者:%@",x);
}];
[subject subscribeNext:^(id x) {
NSLog(@"第二個訂閱者:%@",x);
}];

//3、傳送訊號
[subject sendNext:@1];
[subject sendNext:@2];

可以當通知來使用

也可以當做代理使用(在一個View中定義為屬性,外部訂閱,內容呼叫)


RACRepalySubject

//RACRepalySubject使用步驟
/**
1、建立訊號 [RACReplaySubject subject] ,跟RACSignal不一樣,建立訊號時沒有block
2、可以先傳送訊號,再訂閱訊號,RACSubject不可以!!!
*訂閱訊號 -(RACDisposable)subscribeNext:(void(^)(id x))nextBlock
*傳送訊號 sendNext:(id)value

RACReplaySubject:底層實現和RACSubject不一樣
1、呼叫sendNext傳送訊號,把值儲存起來,然後遍歷剛剛儲存的所有訂閱者,一個一個呼叫訂閱者的nextBlock
2、呼叫subscribeNext訂閱訊號,遍歷所有儲存的值,一個一個呼叫訂閱者的nextBlock

如果想當一個訊號被訂閱,就重複播放之前所有值,需要先傳送訊號,再訂閱訊號
也就是先儲存值,再訂閱值
*/

//1、建立訊號
RACReplaySubject *replaySubject = [RACReplaySubject replaySubjectWithCapacity:3];

//2、傳送訊號
[replaySubject sendNext:@1];
[replaySubject sendNext:@2];

//3、訂閱訊號
[replaySubject subscribeNext:^(id x) {
NSLog(@"第一個訂閱者收到的資料%@",x);
}];
[replaySubject subscribeNext:^(id x) {
NSLog(@"第二個訂閱者收到的資料%@",x);
}];

兩種的輸出為:

/**
// 先訂閱,後傳送訊號
2016-08-29 16:18:23.154 RACDemo[5507:185680] 第一個訂閱者收到的資料1
2016-08-29 16:18:23.154 RACDemo[5507:185680] 第二個訂閱者收到的資料1
2016-08-29 16:18:23.154 RACDemo[5507:185680] 第一個訂閱者收到的資料2
2016-08-29 16:18:23.155 RACDemo[5507:185680] 第二個訂閱者收到的資料2
*/

/**
// 先傳送訊號,再訂閱
2016-08-29 16:19:43.945 RACDemo[5641:186739] 第一個訂閱者收到的資料1
2016-08-29 16:19:43.945 RACDemo[5641:186739] 第一個訂閱者收到的資料2
2016-08-29 16:19:43.946 RACDemo[5641:186739] 第二個訂閱者收到的資料1
2016-08-29 16:19:43.946 RACDemo[5641:186739] 第二個訂閱者收到的資料2
*/

RACSequence

元祖

NSArray

// 遍歷陣列
NSArray *array = @[@1,@2,@3];

// 1、把陣列轉換成集合RACSequence,array.rac_seuqence
// 2、把集合RACSequence轉換RACSignal訊號類,array.rac_sequence.signal
// 3、訂閱訊號,啟用訊號,會自動把集合中的所有值,遍歷出來
[array.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];

NSDictionary

 // 遍歷字典,遍歷出來的鍵值會包裝成RACTuple(元祖物件)
NSDictionary *dic = @{@"name":@"wangtongke",@"age":@18};

[dic.rac_sequence.signal subscribeNext:^(id x) {

// 遍歷字典 X為RAC的元祖(RACTuple)
// 解包元祖,會把元祖的值,按順序給引數裡邊的變數賦值

RACTupleUnpack(NSString *key,NSString *value) = x;

// 以上 相當於一下寫法
// NSString *key1 = x[0];
// NSString *value1 = x[1];

NSLog(@"%@ %@\n",key,value);

}];

RACComand

    /**
1、建立命令 initWithSignalblock:(RACSignal * (^)(id input))signalBlock
2、在signalBlock中,建立RACSignal,並且作為signalBlock的返回值
3、執行命令 -(RACSignal *)execute:(id)input

// 注意事項
1、signalBlock必須要返回一個signal,不能返回nil,
2、如果不想要傳遞訊號,直接建立空的訊號返回[RACSignal empty];
3、RACCommand,如果資料傳遞完畢,必須呼叫[subscriber sendCompleted],這時命令才會執行完畢,否則永遠處於執行中.
4、RACComand需要被強引用,否則接手不到RACCommand中的訊號,因此,RACCommand中的訊號是延遲傳送的。

// 設計思想 : 內部signalBlock為什麼要返回一直訊號,這個訊號有什麼用
1、在RAC開發中,通常會把網路請求封裝到RACCommand,直接執行某個RACCommand就能傳送請求。
2、當RACCommand內部請求到資料的時候,需要把請求的資料傳遞給外界,這時候就需要通過signalBlock返回的訊號傳遞了

// 如何拿到RACCommand中返回訊號發出的資料
1、RACCommand有個執行訊號源executionSignal,這個signal of signal(訊號的訊號),意思是發出的資料是訊號,不是普通的型別
2、訂閱executionSignal就能拿到RACCommand中返回的訊號,然後訂閱signalblock返回的訊號。

// 監聽當前命令是否正在執行executing
// 使用場景 按鈕點選,網路請求
*/

// ******* 1、建立命令
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
NSLog(@"執行命令");
NSLog(@"input---%@",input);
//必須返回訊號 不能返回nil 建立空訊號,
//return [RACSignal empty];

// ******* 2、建立訊號,用來傳遞資料

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

[subscriber sendNext:@"請求資料"];
[subscriber sendNext:@"請求資料1"];
//注意:資料傳遞完,必須呼叫sendCompleted,這時命令才執行完畢
[subscriber sendCompleted];

return [RACDisposable disposableWithBlock:^{

}];
}];
}];

//強引用命令,不然會自動銷燬,接受不到資料
_command = command;

// ******* 3、訂閱RACCommand中的訊號
[command.executionSignals subscribeNext:^(id x) {
[x subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}];

//高階用法
//switchToLatest:用於signal of signals,獲取signal of signals 發出的最新的訊號,也就是可以直接拿到RACCommand中的訊號
[command.executionSignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"高階--%@",x);
}];

// ******** 4、監聽命令是否執行完畢,預設會來一次,可以直接跳過,skip表示跳過第一次訊號
[[command.executing skip:0] subscribeNext:^(id x) {
if ([x boolValue] == YES)
{
//正在執行
NSLog(@"正在執行");
}
else
{
//執行完成
NSLog(@"執行完成");
}
}];

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


RACMulticastConnection

解決多次訂閱多次請求的問題,只有連線之後才呼叫所有訂閱者的subscribeNext

/**
RACMulticastConnection使用步驟

**** 1、建立訊號 +(RACSignal)createSignal
**** 2、建立連線 RACMulticastConnection *connect = [signal publish];
**** 3、訂閱訊號,注意:訂閱的不再是之前的訊號,而是連線的訊號[connect.signal subscribeNext];
**** 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(@"傳送請求111111");
[subscriber sendNext:@1];
return nil;
}];

// ********** 2、訂閱訊號
[signal subscribeNext:^(id x) {
NSLog(@"接受資料11111--%@",x);
}];
// 訂閱兩次訊號
[signal subscribeNext:^(id x) {
NSLog(@"接受資料11111---%@",x);
}];

//會執行兩次傳送請求。也就是每訂閱一次 就會傳送一次請求

//RACMulicastConnection解決重複請求問題
// ********** 1、建立訊號

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

NSLog(@"傳送請求2");
[subscriber sendNext:@1];

return nil;
}];
// ********** 2、 建立連線
RACMulticastConnection *connect = [signal2 publish];

// ********** 3、訂閱訊號
// 注意:訂閱訊號也不能啟用訊號,只是儲存訂閱者到資料,必須通過連線,當呼叫連線,就會一次清呼叫所有訂閱者的sendNext;
[connect.signal subscribeNext:^(id x) {
NSLog(@"訂閱者一訊號");
}];

[connect.signal subscribeNext:^(id x) {
NSLog(@"訂閱者二訊號");
}];

// ********** 4、連線
[connect connect];


bind

/**

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

// bind(繫結)的使用思想和Hook的一樣---> 都是攔截API從而可以對資料進行操作,,而影響返回資料。
// 傳送訊號的時候會來到30行的block。在這個block裡我們可以對資料進行一些操作,那麼35行列印的value和訂閱繫結訊號後的value就會變了。變成什麼樣隨你喜歡嘍。


// ******************方式1、 在返回結果後拼接
// [_textField.rac_textSignal subscribeNext:^(id x) {
// NSLog(@"輸出:%@",x);
// }];

// ******************方式2、在返回結果前拼接,使用RAC中的bind方法做處理
/**
bind方法引數:需要傳入一個返回值是RACSignalBindBlock的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、描述
*/
[[_textField.rac_textSignal bind:^RACStreamBindBlock{

return ^RACStream *(id value,BOOL *stop){
return [RACReturnSignal return:[NSString stringWithFormat:@"輸出:%@",value]];
};
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];


// 1.建立訊號
RACSubject *subject = [RACSubject subject];
// 2.繫結訊號
RACSignal *bindSignal = [subject bind:^RACStreamBindBlock{
// block呼叫時刻:只要繫結訊號訂閱就會呼叫。不做什麼事情,
return ^RACSignal *(id value, BOOL *stop){
// 一般在這個block中做事 ,發資料的時候會來到這個block。
// 只要源訊號(subject)傳送資料,就會呼叫block
// block作用:處理源訊號內容
// value:源訊號傳送的內容,
value = @3; // 如果在這裡把value的值改了,那麼訂閱繫結訊號的值即44行的x就變了
NSLog(@"接受到源訊號的內容:%@", value);
//返回訊號,不能為nil,如果非要返回空---則empty或 alloc init。
return [RACReturnSignal return:value]; // 把返回的值包裝成訊號
};
}];

// 3.訂閱繫結訊號
[bindSignal subscribeNext:^(id x) {

NSLog(@"接收到繫結訊號處理完的訊號:%@", x);
}];
// 4.傳送訊號
[subject sendNext:@"123"];

RACMethod


// ********************** 1、代替代理 **************************

/**
需求: 自定義redView,監聽redView中按鈕點選
之前都是需要通過代理監聽,給紅色view新增一個代理屬性,點選按鈕的時候,通知代理做事情
rac_signalForSelector:把呼叫某個物件的方法的資訊轉換成訊號,只要呼叫這個方法,就會傳送訊號
這裡表示只要redView呼叫btnClick,就會發出訊號,只需要訂閱就可以了

*/
WTKView *view = [[WTKView alloc]initWithFrame:CGRectMake(0, 200, 375, 200)];
[self.view addSubview:view];

[[view rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {
NSLog(@"點選紅色按鈕---%@",x);
//怎麼傳值????
}];

// ********************** 2、KVO **************************
[[view rac_valuesAndChangesForKeyPath:@"center" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
view.center = CGPointMake(100, 100);



// ********************** 3、監聽事件 **************************
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.backgroundColor = [UIColor purpleColor];
btn.frame = CGRectMake(300, 300, 200, 30);
[btn setTitle:@"RAC" forState:UIControlStateNormal];
[self.view addSubview:btn];

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


// ********************** 4、代替通知 **************************
// 把監聽到的通知,轉換成訊號
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
NSLog(@"鍵盤彈出");
}];


// ********************** 5、監聽文字框文字改變 **************************

[self.textField.rac_textSignal subscribeNext:^(id x) {
NSLog(@"文字改變了---%@",x);
}];


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

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

[subscriber sendNext:@"傳送請求1"];


return [RACDisposable disposableWithBlock:^{

}];
}];
RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {


dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[subscriber sendNext:@"傳送請求2"];
});

return [RACDisposable disposableWithBlock:^{

}];
}];

// 使用注意:幾個訊號,引數一的方法就必須有幾個引數,每個引數對應訊號發出的資料
[self rac_liftSelector:@selector(wtkUpdateWithDic1:withDic2:) withSignalsFromArray:@[request1,request2]];


skip

// 跳躍 : 如下,skip傳入2 跳過前面兩個值
//應用場景: 在實際開發中比如 後臺返回的資料前面幾個沒用,我們想跳躍過去,便可以用skip
- (void)skip {
RACSubject *subject = [RACSubject subject];
[[subject skip:2] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@3];
}


distinctUntilChanged

//應用場景:-- 如果當前的值跟上一次的值一樣,就不會被訂閱到
- (void)distinctUntilChanged {
RACSubject *subject = [RACSubject subject];
[[subject distinctUntilChanged] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
// 傳送訊號
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@2]; // 不會被訂閱
}

take

// 應用場景:可以遮蔽一些值,去前面幾個值---這裡take為2 則只拿到前兩個值
- (void)take {
RACSubject *subject = [RACSubject subject];
[[subject take:2] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
// 傳送訊號
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@3];
}

takeLast

//應用場景:和take的用法一樣,不過他取的是最後的幾個值,如下,則取的是最後兩個值
//注意點:takeLast 一定要呼叫sendCompleted,告訴他傳送完成了,這樣才能取到最後的幾個值
- (void)takeLast {
RACSubject *subject = [RACSubject subject];
[[subject takeLast:2] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
// 傳送訊號
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@3];
[subject sendCompleted];
}

takeUntil

//應用場景:---給takeUntil傳的是哪個訊號,那麼當這個訊號傳送訊號或sendCompleted,就不能再接受源訊號的內容了。
- (void)takeUntil {
RACSubject *subject = [RACSubject subject];
RACSubject *subject2 = [RACSubject subject];
[[subject takeUntil:subject2] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
// 傳送訊號
[subject sendNext:@1];
[subject sendNext:@2];
[subject2 sendNext:@3]; // 1
// [subject2 sendCompleted]; // 或2
[subject sendNext:@4];
}

ignore

//應用場景: 忽略掉一些值
- (void)ignore {
//ignore:忽略一些值
//ignoreValues:表示忽略所有的值
// 1.建立訊號
RACSubject *subject = [RACSubject subject];
// 2.忽略一些值
RACSignal *ignoreSignal = [subject ignore:@2]; // ignoreValues:表示忽略所有的值
// 3.訂閱訊號
[ignoreSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
// 4.傳送資料
[subject sendNext:@2];

}

fliter

// 一般和文字框一起用,新增過濾條件
- (void)fliter {
// 只有當文字框的內容長度大於5,才獲取文字框裡的內容
[[self.textField.rac_textSignal filter:^BOOL(id value) {
// value 源訊號的內容
return [value length] > 5;
// 返回值 就是過濾條件。只有滿足這個條件才能獲取到內容
}] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
}

map

- (void)map {
// 建立訊號
RACSubject *subject = [RACSubject subject];
// 繫結訊號
RACSignal *bindSignal = [subject map:^id(id value) {

// 返回的型別就是你需要對映的值
return [NSString stringWithFormat:@"ws:%@", value]; //這裡將源訊號傳送的“123” 前面拼接了ws:
}];
// 訂閱繫結訊號
[bindSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
// 傳送訊號
[subject sendNext:@"123"];

}

flattenMap

應用場景: 主要用於訊號中的訊號

- (void)flattenMap {
// 建立訊號
RACSubject *subject = [RACSubject subject];
// 繫結訊號
RACSignal *bindSignal = [subject flattenMap:^RACStream *(id value) {
// block:只要源訊號傳送內容就會呼叫
// value: 就是源訊號傳送的內容
// 返回訊號用來包裝成修改內容的值
return [RACReturnSignal return:value];

}];

// flattenMap中返回的是什麼訊號,訂閱的就是什麼訊號(那麼,x的值等於value的值,如果我們操縱value的值那麼x也會隨之而變)
// 訂閱訊號
[bindSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
// 傳送資料
[subject sendNext:@"123"];
}

- (void)flattenMap2 {

// 建立訊號
RACSubject *signalofSignals = [RACSubject subject];
RACSubject *signal = [RACSubject subject];

// 訂閱訊號
//方式1
// [signalofSignals subscribeNext:^(id x) {
//
// [x subscribeNext:^(id x) {
// NSLog(@"%@", x);
// }];
// }];
// 方式2
// [signalofSignals.switchToLatest ];
// 方式3
// RACSignal *bignSignal = [signalofSignals flattenMap:^RACStream *(id value) {
//
// //value:就是源訊號傳送內容
// return value;
// }];
// [bignSignal subscribeNext:^(id x) {
// NSLog(@"%@", x);
// }];
// 方式4--------也是開發中常用的
[[signalofSignals flattenMap:^RACStream *(id value) {
return value;
}] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];

// 傳送訊號
[signalofSignals sendNext:signal];
[signal sendNext:@"123"];
}

RAC常見巨集

    // RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用於給某個物件的某個屬性繫結。
RAC(self.label,text) = _textField.rac_textSignal;

// RACObserve(self, name):監聽某個物件的某個屬性,返回的是訊號

[RACObserve(self.label, text) subscribeNext:^(id x) {
NSLog(@"%@",x);
}];

// @weakify(Obj)和@strongify(Obj),一般兩個都是配套使用,在主標頭檔案(ReactiveCocoa.h)中並沒有匯入,需要自己手動匯入,RACEXTScope.h才可以使用。但是每次匯入都非常麻煩,只需要在主標頭檔案自己匯入就好了。
// 最新版庫名 已換成 EXTScope
// 兩個配套使用,先weak再strong
@weakify(self);
// @strongify(self);
[RACObserve(self, label.text) subscribeNext:^(id x) {
@strongify(self);
}];


// RACTuplePack:把資料包裝成RACTuple(元組類)
// 把引數中的資料包裝成元祖
RACTuple *tuple = RACTuplePack(@10,@20);


// RACTupleUnpack:把RACTuple(元組類)解包成對應的資料。
// 把引數再用的資料包裝成元祖

組合

combineLatest

//使用場景: 把多個訊號聚合成你想要的訊號,使用場景----:比如-當多個輸入框都有值的時候按鈕才可點選。
// 思路--- 就是把輸入框輸入值的訊號都聚合成按鈕是否能點選的訊號。
- (void)combineLatest {

RACSignal *combinSignal = [RACSignal combineLatest:@[self.accountField.rac_textSignal, self.pwdField.rac_textSignal] reduce:^id(NSString *account, NSString *pwd){ //reduce裡的引數一定要和combineLatest陣列裡的一一對應。
// block: 只要源訊號傳送內容,就會呼叫,組合成一個新值。
NSLog(@"%@ %@", account, pwd);
return @(account.length && pwd.length);
}];

// // 訂閱訊號
// [combinSignal subscribeNext:^(id x) {
// self.loginBtn.enabled = [x boolValue];
// }]; // ----這樣寫有些麻煩,可以直接用RAC巨集
RAC(self.loginBtn, enabled) = combinSignal;
}

zipWith

//使用場景:把兩個訊號壓縮成一個訊號,只有當兩個訊號同時發出訊號內容時,並且把兩個訊號的內容合併成一個元祖,才會觸發壓縮流的next事件。

- (void)zipWith {
// 建立訊號A
RACSubject *signalA = [RACSubject subject];
// 建立訊號B
RACSubject *signalB = [RACSubject subject];
// 壓縮成一個訊號
// **-zipWith-**: 當一個介面多個請求的時候,要等所有請求完成才更新UI
// 等所有訊號都傳送內容的時候才會呼叫
RACSignal *zipSignal = [signalA zipWith:signalB];
[zipSignal subscribeNext:^(id x) {
NSLog(@"%@", x); //所有的值都被包裝成了元組
}];

// 傳送訊號 互動順序,元組內元素的順序不會變,跟傳送的順序無關,而是跟壓縮的順序有關[signalA zipWith:signalB]---先是A後是B
[signalA sendNext:@1];
[signalB sendNext:@2];

}

merge

// 任何一個訊號請求完成都會被訂閱到
//使用場景:多個訊號合併成一個訊號,任何一個訊號有新值就會呼叫
- (void)merge {
// 建立訊號A
RACSubject *signalA = [RACSubject subject];
// 建立訊號B
RACSubject *signalB = [RACSubject subject];
//組合訊號
RACSignal *mergeSignal = [signalA merge:signalB];
// 訂閱訊號
[mergeSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
// 傳送訊號---交換位置則資料結果順序也會交換
[signalB sendNext:@"下部分"];
[signalA sendNext:@"上部分"];
}

then

//使用場景:有兩部分資料:想讓上部分先進行網路請求但是過濾掉資料,然後進行下部分的,拿到下部分資料
- (void)then {
// 建立訊號A
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 傳送請求
NSLog(@"----傳送上部分請求---afn");

[subscriber sendNext:@"上部分資料"];
[subscriber sendCompleted]; // 必須要呼叫sendCompleted方法!
return nil;
}];

// 建立訊號B,
RACSignal *signalsB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 傳送請求
NSLog(@"--傳送下部分請求--afn");
[subscriber sendNext:@"下部分資料"];
return nil;
}];
// 建立組合訊號
// then;忽略掉第一個訊號的所有值
RACSignal *thenSignal = [signalA then:^RACSignal *{
// 返回的訊號就是要組合的訊號
return signalsB;
}];

// 訂閱訊號
[thenSignal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];

}

concat

//使用場景:有兩部分資料:想讓上部分先執行,完了之後再讓下部分執行(都可獲取值)
- (void)concat {
// 組合

// 建立訊號A
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 傳送請求
// NSLog(@"----傳送上部分請求---afn");

[subscriber sendNext:@"上部分資料"];
[subscriber sendCompleted]; // 必須要呼叫sendCompleted方法!
return nil;
}];

// 建立訊號B,
RACSignal *signalsB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 傳送請求
// NSLog(@"--傳送下部分請求--afn");
[subscriber sendNext:@"下部分資料"];
return nil;
}];


// concat:按順序去連結
//**-注意-**:concat,第一個訊號必須要呼叫sendCompleted
// 建立組合訊號
RACSignal *concatSignal = [signalA concat:signalsB];
// 訂閱組合訊號
[concatSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];

}

網路請求應用場景

 RACCommand *reuqesCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {

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

NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
parameters[@"test"] = @"";

// 傳送請求
[[AFHTTPRequestOperationManager manager] GET:@"https://api.douban.com/v2/book/search" parameters:parameters success:^(AFHTTPRequestOperation * _Nonnull operation, id _Nonnull responseObject) {
NSLog(@"%@",responseObject);

// 請求成功呼叫
// 把資料用訊號傳遞出去
[subscriber sendNext:responseObject];

[subscriber sendCompleted];


} failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) {
// 請求失敗呼叫

}];

return nil;
}];

// 在返回資料訊號時,把資料中的字典對映成模型訊號,傳遞出去
return [requestSignal map:^id(NSDictionary *value) {
NSMutableArray *dictArr = value[@"Persions"];

// 字典轉模型,遍歷字典中的所有元素,全部對映成模型,並且生成陣列
NSArray *modelArr = [[dictArr.rac_sequence map:^id(id value) {

return [Persion bookWithDict:value];
}] array];

return modelArr;
}];

}];


相關文章