RAC(ReactiveCocoa)使用方法(一)
RAC(ReactiveCocoa)使用方法(一)
RAC(ReactiveCocoa)使用方法(二)
什麽是RAC?
最近回顧了一下ReactiveCocoa
的方法,也看了一些人的文章,現寫篇文章總結一下。
現在這個庫最新支持Swift,如果你要是用Cocoapods
的話不指定版本它默認是下載Swift版本,如果依舊想用OC版本就指定一個版本,最好是V2.5版本及以下,否則可能會出現錯誤。最近我試的是V2.5,可以正常使用。
項目中用Cocoapods
使用:pod "ReactiveCocoa", ‘~> 2.5‘
那什麽是RAC勒?想必大家隨便谷歌一下就一大片這個概念和文章。
RAC具有函數響應式編程特性,由Matt Diephouse
部分參考來源
為什麽要使用RAC?
因為RAC具有高聚合低耦合的思想,使用RAC會讓代碼更簡潔,邏輯更清晰。再結合MVVM架構,讓你瞬間爽爆了!
RAC有很多的類,為很多的UI控件都拓展了方法,使得開發大大的簡便化,這裏就簡單的介紹開發過程中用到的方法。
打開應用的初始ViewController,引入ReactiveCocoa的頭文件。
#import <ReactiveCocoa/ReactiveCocoa.h>
在控制器中創建一個TextField,SB拖入更方便,然後如下
[self.TextField.rac_textSignal subscribeNext:^(id x){ NSLog(@"x:%@", x); }];
編譯運行,在輸入框中輸入文字。註意打印信息的輸出應該和下面的類似。
2017-11-29 10:26:25.152197+0800 MVVM-Demo[1089:230607] x:a 2017-11-29 10:26:25.159596+0800 MVVM-Demo[1089:230607] x:ah 2017-11-29 10:26:25.385413+0800 MVVM-Demo[1089:230607] x:ahv 2017-11-29 10:26:25.576558+0800 MVVM-Demo[1089:230607] x:ahva 2017-11-29 10:26:25.764013+0800 MVVM-Demo[1089:230607] x:ahvah 2017-11-29 10:26:25.784379+0800 MVVM-Demo[1089:230607] x:ahvahv 2017-11-29 10:26:25.853596+0800 MVVM-Demo[1089:230607] x:ahvahvj 2017-11-29 10:26:25.868552+0800 MVVM-Demo[1089:230607] x:ahvahvja 2017-11-29 10:26:26.002545+0800 MVVM-Demo[1089:230607] x:ahvahvjav 2017-11-29 10:26:26.062553+0800 MVVM-Demo[1089:230607] x:ahvahvjavj
當你看到這些打印信息,你是不是覺得很神奇,都沒有監聽TextField的方法,它咋就那麽牛逼勒。其實RAC內部就幫你做了許多事情。你只要調用相應控件的RAC方法就可以監聽到它們的狀態了。
那麽它是怎麽監聽怎麽做到的勒?
這裏要講幾個很重要的RAC類,不涉及RAC原理,內部怎麽實現還要大家去閱讀源碼了。
RACSiganl
1、
RACSiganl
信號類,表示將來有數據傳遞,有數據改變,信號內部接收到數據,就會馬上發出數據,外部就可以接收到數據了。就像剛剛上面的例子一樣。
2、默認信號都是冷信號,就是這個值改變了它不會觸發,只有訂閱(調用信號RACSignal的subscribeNext訂閱)了這個信號,這個信號才會變為熱信號(值一改變就觸發),才會觸發。RACSiganl簡單使用:
```
// 1.創建信號
RACSignal siganl = [RACSignal createSignal:^RACDisposable (id// 每當有訂閱者訂閱信號,就會調用block。 // 2.發送信號 [subscriber sendNext:@"我是一個信號??"]; // 如果不在發送數據,最好發送信號完成,內部會自動調用[RACDisposable disposable]取消訂閱信號。 [subscriber sendCompleted]; return [RACDisposable disposableWithBlock:^{ // 當信號發送完成或者發送錯誤,就會自動執行這個block,取消訂閱信號。 // 執行完Block後,當前信號就不在被訂閱了。 NSLog(@"dealloc"); }];
}];
// 3.訂閱信號,才會激活信號.
[siganl subscribeNext:^(id x) {
// block調用時刻:每當有信號發出數據,就會調用block.
NSLog(@"數據: %@",x);
}];
打印如下
```
2017-11-29 11:04:34.383754+0800 MVVM-Demo[1185:379135] 數據:我是一個信號??
2017-11-29 11:04:34.383878+0800 MVVM-Demo[1185:379135] dealloc
RACSubscriber
RACSubscriber
訂閱者,用於發送信號,這是一個協議,不是一個類,只要遵守這個協議,並且實現方法才能成為訂閱者。通過create創建的信號,都有一個訂閱者,幫助他發送數據。RACDisposable
RACDisposable
用於取消訂閱或者清理資源,當信號發送完成或者發送錯誤的時候,就會自動觸發它。 當你不想監聽某個信號時,可以通過它主動取消訂閱信號。RACSubject
RACSubject
信號提供者,自己可以充當信號,又能發送信號。通常用來代替代理,有了它,就不必要定義代理了。RACReplaySubject
RACReplaySubject
重復提供信號類,RACSubject的子類。RACReplaySubject
與RACSubject
區別:
RACReplaySubject
可以先發送信號,在訂閱信號,RACSubject
就不可以。
```- 如果一個信號每被訂閱一次,就需要把之前的值重復發送一遍,使用重復提供信號類。
可以設置capacity數量來限制緩存的value的數量, 即只緩存最新的幾個值。
```
RACReplaySubject使用步驟:
- 創建信號 [RACSubject subject],跟RACSiganl不一樣,創建信號時沒有block。
- 可以先訂閱信號,也可以先發送信號。
- 訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
- 發送信號 sendNext:(id)value
RACReplaySubject:底層實現和RACSubject不一樣。
- 調用sendNext發送信號,把值保存起來,然後遍歷剛剛保存的所有訂閱者,一個一個調用訂閱者的nextBlock。
- 調用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);
}];
打印如下:
2017-11-29 11:02:07.468379+0800 MVVM-Demo[1158:370610] 第一個訂閱者接收到的數據1
2017-11-29 11:02:07.468477+0800 MVVM-Demo[1158:370610] 第一個訂閱者接收到的數據2
2017-11-29 11:02:07.468592+0800 MVVM-Demo[1158:370610] 第二個訂閱者接收到的數據1
2017-11-29 11:02:07.468722+0800 MVVM-Demo[1158:370610] 第二個訂閱者接收到的數據2
RACSubject使用步驟
- 創建信號 [RACSubject subject],跟RACSiganl不一樣,創建信號時沒有block。
- 訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
- 發送信號 sendNext:(id)value
RACSubject:底層實現和RACSignal不一樣。
- 調用subscribeNext訂閱信號,只是把訂閱者保存起來,並且訂閱者的nextBlock已經賦值了。
- 調用sendNext發送信號,遍歷剛剛保存的所有訂閱者,一個一個調用訂閱者的nextBlock。
```
// 1.創建信號
RACSubject *subject = [RACSubject subject];
// 2.訂閱信號
[subject subscribeNext:^(id x) {
// 當信號發出新值,就會調用.
NSLog(@"第一個訂閱者%@",x);
}];
[subject subscribeNext:^(id x) {
// 當信號發出新值,就會調用.
NSLog(@"第二個訂閱者%@",x);
}];
// 3.發送信號
[subject sendNext:@"我是一個信號??"];
* #####RACTuple
`RACTuple `元組類, 類似NSArray,用來包裝值.
* #####RACSequence
`RACSequence `集合類,用於代替NSArray, NSDictionary,可以使用它來快速遍歷數組和字典。
* `RACSequence `和`RACTuple `簡單使用
`NSArray`
NSArray *arr = @[@"哈哈",@"呵呵", @"嘿嘿", @"哼哼"];
[arr.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"x: %@", x);
}];
打印如下
2017-11-29 11:19:27.081935+0800 MVVM-Demo[1267:428560] x: 哈哈
2017-11-29 11:19:27.082227+0800 MVVM-Demo[1267:428560] x: 呵呵
2017-11-29 11:19:27.082350+0800 MVVM-Demo[1267:428560] x: 嘿嘿
2017-11-29 11:19:27.082664+0800 MVVM-Demo[1267:428560] x: 哼哼
`原理:`
* 通過`arr.rac_sequence`把數據arr轉化成集合RACSequence
* 通過`arr.rac_sequence.signal `把集合RACSequence轉化成了信號
*通過subscribeNext訂閱信號,把遍歷集合
`NSDictionary `
// 2.遍歷字典,遍歷出來的鍵值對會包裝成RACTuple(元組對象)
NSDictionary *dict = @{@"name": @"soliloquy", @"age": @26};
[dict.rac_sequence.signal subscribeNext:^(id x) {
// 解包元組,會把元組的值,按順序給參數裏面的變量賦值
RACTupleUnpack(NSString *key,NSString *value) = x;
NSLog(@"%@ %@",key,value);
}];
打印如下
2017-11-29 11:51:08.027070+0800 MVVM-Demo[1367:471752] name soliloquy
2017-11-29 11:51:08.027526+0800 MVVM-Demo[1367:471752] age 26
`字典轉模型`
NSArray arr = @[
@{@"name": @"soliloquy", @"age": @26},
@{@"name": @"ptl", @"age": @21},
];
[arr.rac_sequence.signal subscribeNext:^(id x) {
// 運用RAC遍歷字典,x:字典
Model item = [Model modelWithDict:x];
[array addObject:item];
}];
`其他用法`
NSArray *arr = @[
@{@"name": @"soliloquy", @"age": @26},
@{@"name": @"ptl", @"age": @21},
];
NSArray *ay = [[arr.rac_sequence map:^id(id value) {
return [Persion modelWithDict: value];
}] array];
NSLog(@"ay: %@", ay);
for (Persion *model in ay) {
NSLog(@"%@---%zd", model.name, model.age);
}
打印如下
2017-11-29 12:18:24.024939+0800 MVVM-Demo[1631:553078] ay: (
"
* #####RACCommand
`RACCommand `RAC 中用於處理事件的類,可以把事件如何處理,事件中的數據如何傳遞,包裝到這個類中,他可以很方便的監控事件的執行過程。
比如:監聽按鈕點擊,網絡請求
`RACCommand簡單使用`
一、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],這時命令才會執行完畢,否則永遠處於執行中。
4.RACCommand需要被強引用,否則接收不到RACCommand中的信號,因此RACCommand中的信號是延遲發送的。
三、RACCommand設計思想
1.在RAC開發中,通常會把網絡請求封裝到RACCommand,直接執行某個RACCommand就能發送請求。
2.當RACCommand內部請求到數據的時候,需要把請求的數據傳遞給外界,這時候就需要通過signalBlock返回的信號傳遞了。
四、如何拿到RACCommand中返回信號發出的數據。
1.RACCommand有個執行信號源executionSignals,這個是signal of signals(信號的信號),意思是信號發出的數據是信號,不是普通的類型。
2.訂閱executionSignals就能拿到RACCommand中返回的信號,然後訂閱signalBlock返回的信號,就能獲取發出的值。
五、監聽當前命令是否正在執行executing
六、使用場景, 監聽按鈕點擊,網絡請求
// 1.創建命令 強引用命令,不要被銷毀,否則接收不到數據
self.command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
// 創建空信號,必須返回信號
// return [RACSignal empty];
// 2.創建信號,用來傳遞數據
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSArray *arr = @[@"123",@"321", @"132", @"312"];
[subscriber sendNext:arr];
// 註意:數據傳遞完,最好調用sendCompleted,這時命令才執行完畢。
[subscriber sendCompleted];
return nil;
}];
}];
// 3.訂閱RACCommand中的信號
[command.executionSignals subscribeNext:^(id x) {
[x subscribeNext:^(id x) {
NSLog(@"數據為: %@",x);
}];
}];
```
打印如下
2017-11-29 12:41:50.364091+0800 MVVM-Demo[1844:622694] 數據為: (
123,
321,
132,
312
)
RAC高級用法
// switchToLatest:用於signal of signals,獲取signal of signals發出的最新信號,也就是可以直接拿到RACCommand中的信號
[self.command.executionSignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"x: %@",x);
}];
// 4.監聽命令是否執行完畢,默認會來一次,可以直接跳過,skip表示跳過第一次信號。
[[command.executing skip:1] subscribeNext:^(id x) {
if ([x boolValue] == YES) {
NSLog(@"正在執行");
}else{
NSLog(@"執行完成");
}
}];
// 5.執行命令
[self.conmmand execute:nil];
RACMulticastConnection
RACMulticastConnection
用於當一個信號被多個訂閱時,為了保證創建信號時避免多次調用創建信號中的block造成多次發生數據,可以使用這個該類處理。
RACMulticastConnection
通過RACSignal
的-publish或者-muticast:方法創建.
######RACMulticastConnection使用步驟:
- 創建信號
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
- 創建連接
RACMulticastConnection *connect = [signal publish];
- 訂閱信號,註意:訂閱的不在是之前的信號,而是連接的信號。
[connect.signal subscribeNext:nextBlock];
連接
[connect connect];
// 1.創建請求信號 RACSignal *signal1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSLog(@"發送數據"); return nil; }]; // 2.訂閱信號 [signal1 subscribeNext:^(id x) { NSLog(@"接收數據"); }]; // 2.訂閱信號 [signal1 subscribeNext:^(id x) { NSLog(@"接收數據"); }]; // 3.運行結果,會執行兩遍發送請求,也就是每次訂閱都會發送一次請求 // RACMulticastConnection:解決重復請求問題 // 1.創建信號 RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { NSLog(@"發送請求"); [subscriber sendNext:@"我是數據源"]; return nil; }]; // 2.創建連接 RACMulticastConnection *connect = [signal publish]; // 3.訂閱信號, // 註意:訂閱信號,也不能激活信號,只是保存訂閱者到數組,必須通過連接,當調用連接,就會一次性調用所有訂閱者的sendNext: [connect.signal subscribeNext:^(id x) { NSLog(@"訂閱者一:%@", x); }]; [connect.signal subscribeNext:^(id x) { NSLog(@"訂閱者二:%@", x); }]; // 4.連接,激活信號 [connect connect];
打印如下
```
2017-11-29 13:13:40.735831+0800 MVVM-Demo[2088:721509] 發送數據
2017-11-29 13:13:40.736024+0800 MVVM-Demo[2088:721509] 發送數據
2017-11-29 13:13:40.736550+0800 MVVM-Demo[2088:721509] 發送請求
2017-11-29 13:13:40.736688+0800 MVVM-Demo[2088:721509] 訂閱者一:我是數據源
2017-11-29 13:13:40.736777+0800 MVVM-Demo[2088:721509] 訂閱者二:我是數據源
``可以看出
RACSignal 被調了兩次,每次訂閱一次都會發送請求,這樣就會導致多次請求,而使用
RACMulticastConnection `就只有在創建信號時調了一次。
RACMulticastConnection底層原理:
- 創建connect
connect.sourceSignal -> RACSignal(原始信號) connect.signal -> RACSubject
- 訂閱connect.signal,會調用RACSubject的subscribeNext,創建訂閱者,而且把訂閱者保存起來,不會執行block。
[connect connect]
內部會訂閱RACSignal(原始信號),並且訂閱者是RACSubject
訂閱原始信號,就會調用原始信號中的didSubscribe
didSubscribe,拿到訂閱者調用sendNext,其實是調用RACSubject的sendNext
*RACSubject
的sendNext
,會遍歷RACSubject所有訂閱者發送信號。
拿到第二步所有的訂閱者,調用他們的nextBlock
RAC(ReactiveCocoa)使用方法(一)