以作用為導向去學習一類新技能,首先了解這個是用來做什麼的?
首先ReactiveCocoa在github上的截圖

它的星星相當多,然後它的維護也很勤,更新頻率挺高
在我們iOS開發過程中,當某些事件響應的時候,需要處理某些業務邏輯,這些事件都用不同的方式來處理。
比如按鈕的點擊使用action,ScrollView滾動使用delegate,屬性值改變使用KVO等系統提供的方式。
其實這些事件,都可以通過RAC處理
ReactiveCocoa為事件提供了很多處理方法,而且利用RAC處理事件很方便,可以把要處理的事情,和監聽的事情的代碼放在一起,這樣非常方便我們管理,就不需要跳到對應的方法裡。非常符合我們開發中高聚合,低耦合的思想
1 代替代理:
rac_signalForSelector:用於替代代理。2 代替KVO :
rac_valuesAndChangesForKeyPath:用於監聽某個對象的屬性改變。3 監聽事件:
rac_signalForControlEvents:用於監聽某個事件。4 代替通知:
rac_addObserverForName:用於監聽某個通知。5 監聽文本框文字改變:
rac_textSignal:只要文本框發出改變就會發出這個信號。6 處理當界面有多次請求時,需要都獲取到數據時,才能展示界面
rac_liftSelector:withSignalsFromArray:Signals:當傳入的Signals(信號數組),每一個signal都至少sendNext過一次,就會去觸發第一個selector參數的方法。 使用注意:幾個信號,參數一的方法就幾個參數,每個參數對應信號發出的數據。學習框架首要之處:個人認為先要搞清楚框架中常用的類,在RAC中最核心的類RACSiganl,搞定這個類就能用ReactiveCocoa開發了。
RACSiganl:信號類,一般表示將來有數據傳遞,只要有數據改變,信號內部接收到數據,就會馬上發出數據。
信號類(RACSiganl),只是表示當數據改變時,信號內部會發出數據,它本身不具備發送信號的能力,而是交給內部一個訂閱者去發出。
默認一個信號都是冷信號,也就是值改變了,也不會觸發,只有訂閱了這個信號,這個信號才會變為熱信號,值改變了才會觸發。
如何訂閱信號:調用信號RACSignal的subscribeNext就能訂閱。
// RACSignal:有數據產生的時候,就使用RACSignal
// RACSignal使用步驟: 1.創建信號 2.訂閱信號 3.發送信號
-(void)RACSingal{
RACDisposable *(^disposable)(id subscriber) = ^RACDisposable *(idsubscriber) {
//發送數據
[subscriber sendNext:@110];
return NULL;
};
//1.創建信號(冷信號)
RACSignal *signal = [RACSignal createSignal:disposable];
// RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
// NSLog(@"信號被訂閱!");
// //發送數據
// [subscriber sendNext:@110];
// return NULL;
// }];
//2.訂閱信號(熱信號)
[signal subscribeNext:^(id x) {
// nextBlock調用:只要訂閱者發送數據就會調用
// nextBlock作用:處理數據,展示到UI上面
// x:信號發送的內容
NSLog(@"%@",x);
}];
// 只要訂閱者調用sendNext,就會執行nextBlock
// 只要訂閱RACDynamicSignal,就會執行didSubscribe
// 前提條件是RACDynamicSignal,不同類型信號的訂閱,處理訂閱的事情不一樣
}
RACSubscriber:表示訂閱者的意思,用於發送信號,這是一個協議,不是一個類,只要遵守這個協議,並且實現方法才能成為訂閱者。通過create創建的信號,都有一個訂閱者,幫助他發送數據。
RACDisposable:用於取消訂閱或者清理資源,當信號發送完成或者發送錯誤的時候,就會自動觸發它。
使用場景:不想監聽某個信號時,可以通過它主動取消訂閱信號。
-(void)RACDisposable{
//1.創建信號
RACDisposable *(^disposable)(id subscriber) = ^RACDisposable *(idsubscriber){
//3.發送信號
_subscriber = subscriber;
[subscriber sendNext:@"發送信號123"];
return [RACDisposable disposableWithBlock:^{
NSLog(@"信號被取消訂閱了!");
}];
};
RACSignal *signal = [RACSignal createSignal:disposable];
//2.訂閱信號
RACDisposable *Disposable = [signal subscribeNext:^(id x) {
NSLog(@"獲取到所訂閱的信號:%@",x);
}];
//默認信號發送數據完畢後就會主動取消訂閱,不過如果訂閱者一直存在(成員屬性強引用),就不會自動取消訂閱 除非 手動取消訂閱(dispose)
[Disposable dispose];
}
RACSubject:RACSubject:信號提供者,自己可以充當信號,又能發送信號。
使用場景:通常用來代替代理,有了它,就不必要定義代理了。
// RACSubject使用步驟
// 1.創建信號 [RACSubject subject],跟RACSiganl不一樣,創建信號時沒有block。
// 2.訂閱信號 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
// 3.發送信號 sendNext:(id)value
// RACSubject:底層實現和RACSignal不一樣。
// 1.調用subscribeNext訂閱信號,只是把訂閱者保存起來,並且訂閱者的nextBlock已經賦值了。
// 2.調用sendNext發送信號,遍歷剛剛保存的所有訂閱者,一個一個調用訂閱者的nextBlock。
-(void)RACSubject{
//1.創建信號
RACSubject *subject = [RACSubject subject];
//2.訂閱信號
[subject subscribeNext:^(id x) {
NSLog(@"獲取所訂閱的信號信息:%@",x);
}];
//3.發送信號
[subject sendNext:@"hahaha"];
}
-(void)RACSubject2{
//1.創建信號
RACSubject *subject = [RACSubject subject];
//2.訂閱信號
[subject subscribeNext:^(id x) {
// block調用時刻:當信號發出新值,就會調用.
NSLog(@"第一個訂閱者%@",x);
}];
[subject subscribeNext:^(id x) {
// block調用時刻:當信號發出新值,就會調用.
NSLog(@"第二個訂閱者%@",x);
}];
//3.發送信號
[subject sendNext:@"發送001"];
}
RACReplaySubject:重復提供信號類,RACSubject的子類。
* RACReplaySubject與RACSubject區別:
* RACReplaySubject可以先發送信號,在訂閱信號,RACSubject就不可以。
* 使用場景一:如果一個信號每被訂閱一次,就需要把之前的值重復發送一遍,使用重復提供信號類。
* 使用場景二:可以設置capacity數量來限制緩存的value的數量,即只緩充最新的幾個值。
// 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
// 如果想當一個信號被訂閱,就重復播放之前所有值,需要先發送信號,在訂閱信號。
// 也就是先保存值,在訂閱值。
-(void)RACReplaySubject{
//1.創建信號
RACReplaySubject *replaySubject = [RACReplaySubject subject];
//2.RACReplaySubject 可以先發送信號 RACSubject 就不可以!
[replaySubject sendNext:@"987654321"];
//3.再訂閱信號
[replaySubject subscribeNext:^(id x) {
NSLog(@"獲取所訂閱的信息:%@",x);
}];
}
RACSubject替換代理
需求:
1.給當前控制器添加一個按鈕,modal到另一個控制器界面
2.另一個控制器view中有個按鈕,點擊按鈕,通知當前控制器
步驟一:在第二個控制器.h,添加一個RACSubject代替代理。
#import "ViewController.h" #import "GlobalHeader.h" @interface TwoVC : ViewController @property (nonatomic, strong) RACSubject *delegateSubject; @end
步驟二:監聽第二個控制器按鈕點擊
- (IBAction)method:(id)sender {
NSLog(@"Two操作!!");
// 通知代理
// 判斷代理信號是否有值
if (self.delegateSubject) {
// 有值,才需要通知
[self.delegateSubject sendNext:@"點擊XXX操作!"];
}
}
步驟三:在第一個控制器中,監聽跳轉按鈕,給第二個控制器的代理信號賦值,並且監聽.
- (IBAction)modelTwoVC:(id)sender {
// 創建第二個控制器
TwoVC *twoVC = [[TwoVC alloc] init];
// 設置代理信號
twoVC.delegateSubject = [RACSubject subject];
// 訂閱代理信號
[twoVC.delegateSubject subscribeNext:^(id x) {
NSLog(@"我要進行 %@ 操作!!",x);
}];
[self presentViewController:twoVC animated:YES completion:^{
NSLog(@"1VC->2VC");
}];
}
RACTuple:元組類,類似NSArray,用來包裝值.
RACSequence:RAC中的集合類,用於代替NSArray,NSDictionary,可以使用它來快速遍歷數組和字典。
*遍歷數組和字典:
-(void)RACSequence{
//****************遍歷數組*********************
// 第一步: 把數組轉換成集合RACSequence numbers.rac_sequence
// 第二步: 把集合RACSequence轉換RACSignal信號類,numbers.rac_sequence.signal
// 第三步: 訂閱信號,激活信號,會自動把集合中的所有值,遍歷出來。
// NSArray *numbers = @[@1,@2,@3,@4];
// [numbers.rac_sequence.signal subscribeNext:^(id x) {
// NSLog(@"遍歷數組:%@",x);
// }];
//*****************遍歷字典*********************
//遍歷出來的鍵值對會包裝成RACTuple(元組對象)
NSDictionary *dict = @{@"name":@"qxuewei",
@"age":@"25",
@"phone":@"1851891455"};
[dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
// NSString *key = x[0];
// NSString *value = x[1];
// NSLog(@"遍歷字典:key:%@ <-> value:%@",key,value);
//等同於
//可以用 解包元素宏定義
RACTupleUnpack(NSString *KEY,NSString *VALUE) = x;
NSLog(@"遍歷字典:key:%@ <-> value:%@",KEY,VALUE);
}];
}
利用 RACSequence 字典轉模型
說起字典轉模型,腦海中會浮現出多種方式,直接系統的KVO,Runtime 或者第三方框架 MJExtension 或者使用 RACSeqience
KVO字典轉模型核心代碼
+(instancetype)ModelWithDict:(NSDictionary *)dict{
Model *model = [[Model alloc] init];
//KVO 字典轉模型
[model setValuesForKeysWithDictionary:dict];
return model;
}
使用KVO進行解析
NSMutableArray *arrM = [NSMutableArray array];
//1. KVO
NSArray *localArr = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"flags" ofType:@"plist"]];
[localArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSDictionary *dict = obj;
Model *KVOModel = [Model ModelWithDict:dict];
[arrM addObject:KVOModel];
}];
NSLog(@"解析完的模型數組:%@",arrM);
使用 RACSequence 解析
//2. RACSequence
[localArr.rac_sequence.signal subscribeNext:^(NSDictionary *x) {
Model *model = [Model ModelWithDict:x];
[arrM addObject:model];
}];
RACSequence 高級用法
//3. RACSequence高級用法 會把集合中的所有元素都映射到一個新的對象
arrM = [NSMutableArray arrayWithArray:[[localArr.rac_sequence map:^id(NSDictionary *value) {
return [Model ModelWithDict:value];
}] array]];
小結:RAC的用法
1.代替代理
//1.RAC作用一:代替代理
-(void)RAC_Delegate{
[[self.grayView rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {
NSLog(@"按鈕被點擊了!");
}];
}
外加上述方法^
2.代理KVO
//2.RAC作用二:代理KVO
//*首先需要導入頭文件 #import "NSObject+RACKVOWrapper.h"
-(void)RAC_KVO1{
[self.grayView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
//代理系統KVO
NSLog(@"grayView 的Frame發生了改變! 變為:value:%@",value);
}];
}
KVO的第二種實現可以不導入頭文件 #import “NSObject+RACKVOWrapper.h”
-(void)RAC_KVO2{
[[self.grayView rac_valuesForKeyPath:@"frame" observer:nil] subscribeNext:^(id x) {
NSLog(@"grayView 的frame發生的改變,變成了:%@",x);
}];
}
兩個方法有區別,1方法在設置監聽以後只在所監聽的值改變時調用,2方法在所有監聽值改變都會調用.
比如同樣在
- (void)viewDidLoad {
[super viewDidLoad];
[self RAC_KVO1];
[self RAC_KVO2];
[self.grayView setFrame:CGRectZero];
}

單獨執行
[self RAC_KVO2];
[self.grayView setFrame:CGRectZero];

可以看出不同
3.監聽事件
//3.RAC作用三:監聽事件 直接代替系統監聽方法
-(void)RAC_Event{
[[_BTN rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"按鈕被點擊了!");
}];
}
4.代替通知
//4.RAC作用四:代替通知
-(void)RAC_Noti{
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidShowNotification object:nil] subscribeNext:^(id x) {
NSLog(@"監聽鍵盤彈出的通知!");
}];
}
5.監聽文本框內值的改變
//5.RAC作用五:監聽文本框輸入
-(void)RAC_{
[_textField.rac_textSignal subscribeNext:^(id x) {
NSLog(@"當前輸入框值:%@",x);
}];
}