導(dǎo)語:
之前一直看我?guī)煾冈谘芯宽憫?yīng)式編程,但是在項(xiàng)目中一直沒有用到,然后我也懶得看,(對,我沒有上進(jìn)心??)。這段時間閑來無事,簡單的研究了一下,百閑之中整理出了文檔。
ReactiveCocoa,很神奇的一個框架,為我們提供了很多方便的方法,去處理事件的操作,如UITextField不需要實(shí)現(xiàn)其代理方法就可以獲取到輸入框中的內(nèi)容。RAC的主要好處是它提供了一個信號RACSignal類,來統(tǒng)一處理Cocoa的各種行為,包括delegate,block,target-action機(jī)制,notification和KVO等等,可以把要處理的事件和監(jiān)聽的代碼放在一起,使用起來也及其簡便易懂。
它在5.0以后詳細(xì)的拆分為了四個庫:ReactiveCocoa、ReactiveSwift、ReactiveObjC、ReactiveObjCBridge。
其中ReactiveCocoa適用于純Swift項(xiàng)目;
ReactiveObjC適用于純OC項(xiàng)目;
本文主要講下ReactiveObjC的應(yīng)用,swift目前項(xiàng)目中還沒有普及,等普及之后再研究啦。
導(dǎo)入
1.手動導(dǎo)入,github上都有地址,自己去下載導(dǎo)入 (https://github.com/ReactiveCocoa/ReactiveCocoa)
2.pods導(dǎo)入,這里不是導(dǎo)入 ReactiveCocoa,而是ReactiveObjC,一定不要導(dǎo)入錯了
導(dǎo)入注意事項(xiàng)
1.若項(xiàng)目為Swift,則導(dǎo)入ReactiveCocoa
2.若項(xiàng)目為Swift,則導(dǎo)入ReactiveObjC
3.若項(xiàng)目為Swift和OC混編,則需要將ReactiveObjC和ReactiveCocoa都導(dǎo)入,同時需導(dǎo)入ReactiveObjCBridge,3個庫都是可以用pods導(dǎo)入的
推薦使用pods
platform: 'ios', '8.0'
pod 'ReactiveObjC'
使用
不管是ReactiveCocoa還是ReactiveObjC,其核心就是信號,也就是事件流。
一.創(chuàng)建信號
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//發(fā)送信號
[subscriber sendNext:@"我要發(fā)送信號啦"];
//如果不再發(fā)送信號,最好發(fā)送信號完成,內(nèi)部會自動調(diào)用[RACdisposable disposable]取消訂閱信號
[subscriber sendCompleted];
return nil;
}];
//訂閱信號, 才會激活信號,信號激活了,才會發(fā)出去,輸出的信號為 “我要發(fā)送信號啦”
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"訂閱信號:%@", x);
}];
//或者利用block回調(diào)
RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//發(fā)送信號
[subscriber sendNext:@"發(fā)送信號2"];
//如果不再發(fā)送信號,最好發(fā)送信號完成,內(nèi)部會自動調(diào)用[RACdisposable disposable]取消訂閱信號
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
//block調(diào)用時刻,當(dāng)信號發(fā)送完成后或者發(fā)送錯誤,就會自動執(zhí)行這個block,取消訂閱信號
//執(zhí)行完block后,當(dāng)前信號就不在被訂閱了
NSLog(@"信號被銷毀");
}];
}];
二.RACSubject使用
//創(chuàng)建信號
RACSubject *subject = [RACSubject subject];
//發(fā)送信號
[subject sendNext:@"發(fā)送信號"];
//訂閱信號
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"信號內(nèi)容%@", x);
}];
三.RACReplaySubject使用
//1.創(chuàng)建信號
RACReplaySubject *replaySub = [RACReplaySubject subject];
//2.創(chuàng)建訂閱者
[replaySub subscribeNext:^(id _Nullable x) {
NSLog(@"哈哈");
}];
//3.發(fā)送訂閱者信號
[replaySub sendNext:@"123456"];
//RACReplaySubject 發(fā)送數(shù)據(jù)
//1.保存值
//2.遍歷所有的訂閱者,發(fā)送數(shù)據(jù)
//3. 第3條可以放在第2條的前面,即可以先發(fā)送,再訂閱。RACSubject不可以調(diào)整
//RACReplaySubject與RACSubject區(qū)別:RACReplaySubject可以先發(fā)送信號,在訂閱信號,RACSubject就不可以。
//使用場景一:如果每個信號被重復(fù)訂閱一次,就需要把之前的值重復(fù)發(fā)送一次,使用重復(fù)提供信號
//使用場景二:可以設(shè)置capacity數(shù)量來限制緩存的value的數(shù)量,即只緩沖最新的幾個
四.RACTuple元組
RAC的元祖,跟我們OC的數(shù)組其實(shí)是一樣的,它其實(shí)就是封裝了我們OC的數(shù)組
//創(chuàng)建元祖
RACTuple *tuple1 = [RACTuple tupleWithObjects:@"one", @"two", @"three", @"four", nil];
//從別的數(shù)組中獲取內(nèi)容
NSArray *array = @[@"five", @"six", @"seven", @"eight"];
RACTuple *tuple2 = [RACTuple tupleWithObjectsFromArray:array];
//利用RAC宏快速封裝,即把數(shù)據(jù)包裝成元組
RACTuple *tuple3 = RACTuplePack(@"nine", @"ten", @"eleven", @"twelve");
NSLog(@"取第一個內(nèi)容1:%@", tuple1[0]);
NSLog(@"取第一個內(nèi)容2:%@", [tuple2 first]);
NSLog(@"取第一個內(nèi)容3:%@", [tuple3 last]);
五.RACTupleUnpack
把RACTuple元組類解包成對應(yīng)的數(shù)據(jù)
//解包元組,會把元組的值,按照順序賦給參數(shù)里面的變量賦值,即name值為Damon,age值為33
RACTuple *tuple4 = RACTuplePack(@"Damon", @"33");
RACTupleUnpack(NSString *name, NSString *age) = tuple4;
六.遍歷ARRAY數(shù)組和Dictionary字典
遍歷ARRAY數(shù)組和Dictionary字典
NSArray *arrayFruit = @[@"apple", @"banana", @"watermelon", @"lemon"];
[arrayFruit.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"數(shù)組內(nèi)容:%@", x);
}];
NSDictionary *dictionary = @{@"紅色":@"red", @"綠色":@"green", @"紫色":@"purple", @"青色":@"cyan",};
[dictionary.rac_sequence.signal subscribeNext:^(id _Nullable x) {
//普通用法
NSString *key1 = x[0];
NSString *value1 = x[0];
NSLog(@"%@--%@", key1, value1);
//高級用法
//x 是一個元祖,這個宏能夠?qū)ey 和 value拆開
RACTupleUnpack(NSString *key, NSString *value) = x;
NSLog(@"字典內(nèi)容%@:%@", key,value);
}];
或者使用RAC集合,即數(shù)組轉(zhuǎn)RAC集合
RACSequence *sequence = arrayFruit.rac_sequence;
//把集合轉(zhuǎn)為信號
RACSignal *racSignal = sequence.signal;
//訂閱集合信號,內(nèi)部會自動遍歷所有的元素發(fā)出來
[racSignal subscribeNext:^(id _Nullable x) {
NSLog(@"11--%@", x);
}];
七.數(shù)組內(nèi)容全部替換為0
第一個是單個操作,第二個是一次性全部置換,2個方法都不會改變原數(shù)組內(nèi)容,操作完成后都會生成一個新的數(shù)組,省去了創(chuàng)建可變數(shù)組然后遍歷出來單個添加的操作
/*內(nèi)容單個替換*/
NSArray *aryChange1 = @[@"11", @"22", @"33", @"44"];
NSArray *newAryChange1 = [[array.rac_sequence map:^id _Nullable(id _Nullable value) {
//將所有內(nèi)容替換為0
return @"0";
}] array];
NSLog(@"單個替換的數(shù)組為:%@", newAryChange1);
/*內(nèi)容一次性替換*/
NSArray *aryChange2 = @[@"111", @"222", @"333", @"444"];
NSArray *newAryChange2 = [[aryChange2.rac_sequence mapReplace:@"0"] array];
NSLog(@"一次性替換的數(shù)組為:%@", newAryChange2);
八.監(jiān)聽UITextField的輸入改變
可以省去設(shè)置delegate 和實(shí)現(xiàn)代理方法的步驟
//監(jiān)聽TextField的輸入(內(nèi)容改變就會調(diào)用)
UITextField *textField = [[UITextField alloc] init];
textField.frame = CGRectMake(20, 100, 200, 60);
textField.backgroundColor = [UIColor grayColor];
[self.view addSubview:textField];
RACSignal *signalText = [textField rac_textSignal];
[signalText subscribeNext:^(id _Nullable x) {
NSLog(@"輸入框內(nèi)容1:%@", x);
}];
//或者添加監(jiān)聽條件
[[textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
//表示文字長度 大于5時才會調(diào)用下面的block
return value.length > 5;
}] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"輸入框內(nèi)容2%@", x);
}];
九.監(jiān)聽button事件
可以省去addTarget添加事件和創(chuàng)建方法的步驟
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(10, 200, 50, 50);
btn.backgroundColor = [UIColor greenColor];
[self.view addSubview:btn];
[[btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
//點(diǎn)擊時觸發(fā)的事件
NSLog(@"按鈕被點(diǎn)擊了 %@", x);
}];
十.登錄按鈕狀態(tài)實(shí)時監(jiān)聽
下面表示只有用戶名和密碼輸入框內(nèi)容都大于0時,登錄按鈕才可以點(diǎn)擊,而且狀態(tài)是實(shí)時監(jiān)聽的,一句代碼就能完成這個功能
RAC(loginBtn, enabled) = [RACSignal combineLatest:@[user.rac_textSignal, psd.rac_textSignal] reduce:^id _Nonnull{
return @(user.text.length && psd.text.length);
}];
十一.監(jiān)聽notification通知事件
可以省去在-(void)dedalloc{}中清除通知和監(jiān)聽通知創(chuàng)建方法的步驟
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
NSLog(@"鍵盤彈起 %@", x);
}];
十二.代替delegate代理方法
可以省去監(jiān)聽以及設(shè)置delegate的步驟,下面表示只要view中執(zhí)行了clickMe這個方法,就會發(fā)送信號執(zhí)行這個回調(diào)
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[[button rac_signalForSelector:@selector(clickMe)] subscribeNext:^(RACTuple * _Nullable x) {
NSLog(@"按鈕被點(diǎn)擊了");
}];
十三.代替KVO監(jiān)聽
可以代替KVO監(jiān)聽,下面表示把監(jiān)聽view的frame屬性改變轉(zhuǎn)換成信號,只要值改變就會發(fā)送信號
UILabel *label = [[UILabel alloc] init];
label.frame = CGRectMake(120, 350, 100, 40);
label.text = @"我是label";
[self.view addSubview:label];
// OC 寫法
// [label addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
[[label rac_valuesForKeyPath:@"frame" observer:self] subscribeNext:^(id _Nullable x) {
NSLog(@"label的屬性改變了:%@", x);//x是監(jiān)聽屬性的改變結(jié)果
}];
//或者這樣寫
// [RACObserve(label, frame) subscribeNext:^(id _Nullable x) {
// NSLog(@"屬性的改變 %@", x);
// }];
十四.代替NSTimer計(jì)時器
self.disposable = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
NSLog(@"當(dāng)前時間:%@", x);
//關(guān)閉計(jì)時器
[self.disposable dispose];
}];
以上基本上就是RAC常用的方法,
參考文檔:
1.http://www.itdecent.cn/p/0845b1a07bfa
2.http://www.itdecent.cn/p/68ae979ba814?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation