以作用為導向去學習一類新技能,首先了解這個是用來做什麼的?
首先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)(idsubscriber) = ^RACDisposable *(id subscriber) { //發送數據 [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)(idsubscriber) = ^RACDisposable *(id subscriber){ //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); }]; }