RAC 指的就是 RactiveCocoa 骂租,基于函數(shù)式響應(yīng)式編程思想的Objective-C實踐,是 Github 的一個開源框架读串,能夠幫我們提供大量方便的事件處理方案聊记,讓我們更簡單粗暴地去處理事件.使用MVVM框架就不得不提RAC.RAC具有高聚合低耦合的思想博投,使用RAC會讓代碼更簡潔里烦,邏輯更清晰际插。
RAC幾乎接管了Apple所有的事件機制,由于RAC將Cocoa中KVO哼审、UIKitEvent谐腰、delegate孕豹、selector等都增加了RAC支持,所以都不用去做很多跨函數(shù)的事怔蚌。
ReactiveCocoa主要包含四個組件:
信號源:RACStream 及其子類巩步;
訂閱者:RACSubscriber 的實現(xiàn)類及其子類;
調(diào)度器:RACScheduler 及其子類桦踊;
清潔工:RACDisposable 及其子類椅野。
而信號源是最核心的部分,其它所有組件都是圍繞它運作的籍胯。
RAC 的核心思想:創(chuàng)建信號 - 訂閱信號 - 發(fā)送信號
1. RACSignal 信號類
表示將來有數(shù)據(jù)傳遞竟闪,有數(shù)據(jù)改變,信號內(nèi)部接收到數(shù)據(jù)杖狼,就會馬上發(fā)出數(shù)據(jù)炼蛤,外部就可以接收到數(shù)據(jù)了。默認信號都是冷信號蝶涩,就是這個值改變了它不會觸發(fā)理朋,只有訂閱(調(diào)用信號RACSignal的subscribeNext訂閱)了這個信號,這個信號才會變?yōu)闊嵝盘?值一改變就觸發(fā))绿聘,才會觸發(fā)嗽上。
/* 創(chuàng)建信號 */
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//RACSubscriber訂閱者對象,用于發(fā)送信號熄攘,這是一個協(xié)議兽愤,不是一個類,只要遵守這個協(xié)議挪圾,并且實現(xiàn)方法才能成為訂閱者浅萧。通過create創(chuàng)建的信號,都有一個訂閱者哲思,幫助他發(fā)送數(shù)據(jù)洼畅。
/* 發(fā)送信號 */
[subscriber sendNext:@"發(fā)送信號"];
return [RACDisposable disposableWithBlock:^{
// 當(dāng)信號發(fā)送完成或者發(fā)送錯誤,就會自動執(zhí)行這個block,取消訂閱信號棚赔。
// 執(zhí)行完Block后土思,當(dāng)前信號就不在被訂閱了。
NSLog(@"dealloc");
}];
//return nil;
}];
//接收信號
[signal subscribeNext:^(id _Nullable x) {
}];
也可以使用RACDisposable類直接取消訂閱
/* 訂閱信號 */
RACDisposable *disposable = [signal subscribeNext:^(id _Nullable x) {
//RACDisposable用于取消訂閱或者清理資源忆嗜,當(dāng)信號發(fā)送完成或者發(fā)送錯誤的時候己儒,就會自動觸發(fā)它。 當(dāng)你不想監(jiān)聽某個信號時捆毫,可以通過它主動取消訂閱信號闪湾。
NSLog(@"信號內(nèi)容:%@", x);
}];
/* 取消訂閱 */
[disposable dispose];
2. 定時器timer(在子線程執(zhí)行)
[[RACSignal interval:1.0 onScheduler:[RACScheduler scheduler]] subscribeNext:^(NSDate * _Nullable x) {
NSLog(@"當(dāng)前時間:%@", x); // x 是當(dāng)前的系統(tǒng)時間
//關(guān)閉計時器就是取消訂閱
//[disposable dispose];
}];
3. RACSubject 信號提供者
RACSubject自己可以充當(dāng)信號,又能發(fā)送信號绩卤。(和代理的用法類似途样,通常用來代替代理江醇,有了它,就不必定義代理了)
/* 創(chuàng)建信號 */
RACSubject *subject = [RACSubject subject];
/* 訂閱信號(通常在別的視圖控制器中訂閱何暇,與代理的用法類似) */
[subject subscribeNext:^(id _Nullable x) {
/*
1創(chuàng)建訂閱者對象
2將block放到訂閱者對象中
3將訂閱者對象放到subscribes數(shù)組里面
*/
NSLog(@"信號內(nèi)容:%@", x);
}];
/* 發(fā)送信號 */
/*
其內(nèi)部的真實操作是遍歷信號對象中的數(shù)組,取出訂閱者,調(diào)用訂閱者中的block執(zhí)行
*/
[subject sendNext:@"發(fā)送信號"];
4. RACTuple 元祖(和 OC 的數(shù)組其實是一樣的陶夜,其實就是封裝了我們 OC 的數(shù)組)
/* 創(chuàng)建元祖 */
RACTuple *tuple1 = [RACTuple tupleWithObjects:@"1", @"2", @"3", @"4", @"5", nil];
/* 從別的數(shù)組中獲取內(nèi)容 */
RACTuple *tuple2 = [RACTuple tupleWithObjectsFromArray:@[@"1", @"2", @"3", @"4", @"5"]];
/* 利用 RAC 宏快速封裝 */
RACTuple *tuple3 = RACTuplePack(@"1", @"2", @"3", @"4", @"5");
NSLog(@"取元祖內(nèi)容:%@", tuple1[0]);
NSLog(@"第一個元素:%@", [tuple2 first]);
NSLog(@"最后一個元素:%@", [tuple3 last]);
這里再說一下RACSequence這個集合類,用于代替NSArray, NSDictionary,可以使用它來快速遍歷數(shù)組和字典。
//遍歷數(shù)組
/*
遍歷原理:
通過arr.rac_sequence把數(shù)據(jù)arr轉(zhuǎn)化成集合RACSequence
通過arr.rac_sequence.signal把集合RACSequence轉(zhuǎn)化成了信號
*通過subscribeNext訂閱信號裆站,遍歷集合
*/
NSArray *array = @[@"1", @"2", @"3", @"4", @"5"];
[array.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"數(shù)組內(nèi)容:%@", x); // x 可以是任何對象
}];
/* 遍歷字典 */
//遍歷字典,遍歷出來的鍵值對會包裝成RACTuple(元組對象)
NSDictionary *dictionary = @{@"key1":@"value1", @"key2":@"value2", @"key3":@"value3"};
[dictionary.rac_sequence.signal subscribeNext:^(RACTuple * _Nullable x) {
//RACTupleUnpack是一個宏定義
RACTupleUnpack(NSString *key, NSString *value) = x; // x 是一個元祖条辟,這個宏能夠?qū)?key 和 value 拆開
NSLog(@"字典內(nèi)容:%@:%@", key, value);
}];
//下面兩個方法都是將數(shù)組內(nèi)容全部換為 0 ,第一個是單個操作宏胯,第二個是一次性全部替換羽嫡,兩個方法都不會改變原數(shù)組內(nèi)容,操作完后都會生成一個新的數(shù)組肩袍,省去了創(chuàng)建可變數(shù)組然后遍歷出來單個添加的步驟杭棵。
/* 內(nèi)容操作 */
NSArray *array1 = @[@"1", @"2", @"3", @"4", @"5"];
NSArray *newArray1 = [[array1.rac_sequence map:^id _Nullable(id _Nullable value) {
NSLog(@"數(shù)組內(nèi)容:%@", value);
return @"0"; // 將所有內(nèi)容替換為 0
}] array];
/* 內(nèi)容快速替換 */
NSArray *array2 = @[@"1", @"2", @"3", @"4", @"5"];
NSArray *newArray2 = [[array.rac_sequence mapReplace:@"0"] array]; // 將所有內(nèi)容替換為 0
5. 代理
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"標題" message:@"123456" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"other", nil];
//可以省去監(jiān)聽以及設(shè)置 delegate 的步驟
[[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple *tuple) {
NSLog(@"%@",tuple.first);
NSLog(@"%@",tuple.second);
NSLog(@"%@",tuple.third);
}];
[alertView show];
6. 通知
//RAC將收到通知后的方法封裝成了block,省去了在 dealloc 中清除通知和監(jiān)聽通知創(chuàng)建方法的步驟。
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification *notification) {
NSLog(@"%@", notification.name);
NSLog(@"%@", notification.object);
}];
7. KVO
UIScrollView *scrolView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
scrolView.contentSize = CGSizeMake(200, 800);
scrolView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrolView];
//原生,其缺點是當(dāng)你需要觀察很多屬性時,你要寫很多次下面的方法,而且在觀察的方法中還要去判斷KeyPath具體是什么
[scrolView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
//RAC方式1 利用 RAC 的宏,只要對象的屬性發(fā)生改變就會產(chǎn)生信號
[RACObserve(scrolView, contentOffset) subscribeNext:^(id x) {
NSLog(@"contentOffset: %@",x);
}];
//RAC方式2
[self.redView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:self block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
}];
//RAC方式3 先包裝成了一個信號,只要值改變就會發(fā)送信號氛赐。
[[self.redView rac_valuesForKeyPath:@"frame" observer:self] subscribeNext:^(id _Nullable x) {
NSLog(@"frame屬性的改變1:%@", x); // x 是監(jiān)聽屬性的改變結(jié)果
}];
8. 監(jiān)聽 TextField 的輸入改變
//可以省去設(shè)置 delegate 和實現(xiàn)代理方法的步驟魂爪。
[[self.textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
return value.length > 5; // 表示輸入文字長度 > 5 時才會調(diào)用下面的 block
}] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"輸入框內(nèi)容:%@", x);
}];
[[self.textField.rac_textSignal map:^id(id value) {
return [UIColor redColor];
}] subscribeNext:^(id x) {
// NSLog(@"x:%@", x);
}];
[self.textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
NSLog(@"輸入框內(nèi)容:%@", x);
}];
//給label同時賦值,其實就是用綁定信號的,只要產(chǎn)生信號內(nèi)容,就會把內(nèi)容給屬性賦值
UILabel *label;
RAC(label,text) = self.textField.rac_textSignal;
9. 添加手勢
//UIView
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init];
[tap.rac_gestureSignal subscribeNext:^(id x) {
NSLog(@"x:%@", x);
self.redView.backgroundColor = [UIColor yellowColor];
}];
[self.redView addGestureRecognizer: tap];
10. 監(jiān)聽 Button 點擊事件
//可以省去 addTarget 添加事件和創(chuàng)建方法的步驟。
[[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"點擊了按鈕");
}];
//登錄按鈕狀態(tài)實時監(jiān)聽
//下面表示只有 用戶名 和 密碼 輸入框內(nèi)容都大于 0 時艰管,登錄 按鈕才可以點擊滓侍,而且狀態(tài)是實時監(jiān)聽的,一句代碼就能完成這個功能蛙婴。
RAC(self.btn, enabled) = [RACSignal combineLatest:@[self.textField.rac_textSignal, self.textField.rac_textSignal] reduce:^id _Nullable(NSString * username, NSString * password){
return @(username.length && password.length);
}];