RactiveCocoa
,是 Github 的一個開源框架坷随,能夠幫我們提供大量方便的事件處理方案房铭,讓我們更簡單粗暴地去處理事件,現(xiàn)在分為 ReactiveObjC
和 ReactiveSwift
甸箱,兩個框架的功能使用相似育叁,本文主要是針對ReactiveObjC
做一個學(xué)習(xí)記錄。
RAC
雖然最大的優(yōu)點是提供了一個單一的芍殖、統(tǒng)一的方法去處理異步的行為豪嗽,包括delegate
方法,blocks
回調(diào)target-action
機制,notifications
和KVO
.
RACSignal的使用及底層實現(xiàn)
1、創(chuàng)建信號:把didSubscribe
(代碼塊)保存到信號中
2豌骏、訂閱信號:調(diào)用signal的subscribeNext:(void (^)(id x))nextBlock
3龟梦、調(diào)用RACDynamicSignal
的didSubscribe
把訂閱者傳遞過去
4、[subscriber sendNext:@1];
調(diào)用訂閱者的發(fā)送消息的方法窃躲,發(fā)送消息
5计贰、執(zhí)行nextBlock
這個代碼塊
RACSignal
RACSignal是信號類,當(dāng)被訂閱后蒂窒,該信號會傳遞改變的數(shù)據(jù)躁倒,可以傳遞一下三種狀態(tài)
-
sendNext(id):
傳遞正確的數(shù)據(jù),告訴訂閱者下一步要干的事情 -
sendError:
傳遞數(shù)據(jù)錯誤洒琢,告訴訂閱者錯誤 -
sendCompleted:
告訴訂閱者發(fā)送完成秧秉,信號又變成冷信號了 -
RACDisposable
對象,這個的作用就是衰抑,可以用來手動移除訂閱 -
subscribeOn
指定訂閱后立即執(zhí)行的block在哪個線程中執(zhí)行 -
deliveOn
指定subscribeNext在指定線程中執(zhí)行
- (void)useSingle{
//創(chuàng)建信號并發(fā)送消息
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"將消息發(fā)送給訂閱著"];
[subscriber sendError:[NSError errorWithDomain:@"給訂閱者發(fā)送錯誤信息" code:1001 userInfo:nil]];
[subscriber sendCompleted];//告訴訂閱者已經(jīng)發(fā)送完成象迎,跟sendError不能同時使用
return nil;//需要手動移除訂閱的話這里就返回RACDisposable類型的對象
return [RACDisposable disposableWithBlock:^{
//需要手動移除的時候使用這個return
}];
}];
//創(chuàng)建訂閱者
RACDisposable *disposable = [signal subscribeNext:^(id x) {
//處理發(fā)送過來的消息
}error:^(NSError *error) {
//錯誤
}completed:^{
//完成
}];
[disposable dispose];//手動移除訂閱
}
總結(jié):
-
createSignal
的block在創(chuàng)建的時候是冷信號不會掉用,當(dāng)訂閱者subscribeNext
之后才會把冷信號變成熱信號呛踊,從而執(zhí)行createSignal的block的block
-
sendNext
執(zhí)行完之后才會調(diào)用subscribeNext 的block
RACSubject
RACSubject
繼承自RACSignal
,是熱信號砾淌,既可以充當(dāng)信號也可以發(fā)送信號啦撮,并不確定什么時候終止
// 1.創(chuàng)建信號
RACSubject *subject = [RACSubject subject];
// 2.訂閱信號
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"x = %@", x);
}];
// 3.發(fā)送數(shù)據(jù)
[subject sendNext:@10];
總結(jié):
- 創(chuàng)建subject的時候,內(nèi)部會創(chuàng)建一個數(shù)組subscribers用來存放所有的訂閱者
- 訂閱信息的時候會創(chuàng)建訂閱者汪厨,并保存到數(shù)組中
- 會根據(jù)訂閱者存入subscribers的順序依次發(fā)送消息
RACSubject可以被多次訂閱赃春,并且只能是先訂閱后發(fā)布,因為訂閱者收不到訂閱之前的消息
RACReplaySubject
RACReplaySubject旨在解決RACSubject不能先發(fā)送消息后訂閱的問題
// 1.創(chuàng)建信號
RACReplaySubject *replaySubject = [RACReplaySubject subject];
// 2.發(fā)送數(shù)據(jù)
[replaySubject sendNext:@10];
// 3.訂閱信號
[replaySubject subscribeNext:^(id _Nullable x) {
NSLog(@"x = %@", x);
}];
實現(xiàn)原理:
-
RACReplaySubject
繼承自RACSubject
,在創(chuàng)建對象的時候骄崩,在父類的基礎(chǔ)上會創(chuàng)建一個數(shù)組保存要發(fā)送的數(shù)據(jù)聘鳞,且在subscribers
中的訂閱者全部都發(fā)送數(shù)據(jù)之后,就會刪除當(dāng)前要發(fā)送的數(shù)據(jù) - 訂閱者
訂閱時才會發(fā)送數(shù)據(jù)
- 訂閱信號之后要拂,先
遍歷一次保存數(shù)據(jù)的數(shù)組
抠璃,然后發(fā)送數(shù)據(jù)
RACCommand
主要用于網(wǎng)絡(luò)請求或者UI交互后立即執(zhí)行的流程(
eg:textfield輸入內(nèi)容后處理
)
初始化方法
- (instancetype)initWithSignalBlock:(RACSignal<ValueType> * (^)(InputType _Nullable input))signalBlock;
-
- (instancetype)initWithEnabled:(nullable RACSignal<NSNumber *> *)enabledSignal signalBlock:(RACSignal<ValueType> * (^)(InputType _Nullable input))signalBlock;
執(zhí)行方法 - (RACSignal<ValueType> *)execute:(nullable InputType)input;
- (void)RACCommandTest {
// 1. 創(chuàng)建命令 - initWithSignalBlock
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
NSLog(@"input:%@",input);
// 注意: 不能返回空的信號
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"input命令來了, 開始發(fā)送數(shù)據(jù)"];
return nil;
}];
}];
// 2. 執(zhí)行命令
RACSignal *signal = [command execute:@"發(fā)送input 命令, 執(zhí)行command"];
// 3. 訂閱信號
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"數(shù)據(jù): %@",x);
}];
}
RACCommand屬性后期添加
RAC---rac_liftselector
這個方法的主要作用就是保證多個信號都返回數(shù)據(jù)才會執(zhí)行對應(yīng)的方法(
調(diào)用多個接口,拿到所有的返回值后刷新UI
)
RACSignal *firstSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"第一次獲取到的網(wǎng)絡(luò)數(shù)據(jù)"];
return nil;
}];
RACSignal *secondSignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"第二次獲取到的網(wǎng)絡(luò)數(shù)據(jù)"];
return nil;
}];
//updateUIWithData1:Data2:在所有信號都返回數(shù)據(jù)后執(zhí)行
//參數(shù)data1脱惰,data2必須與firstSignal, secondSignal相對應(yīng)
[self rac_liftSelector:@selector(updateUIWithData1:Data2:) withSignalsFromArray:@[firstSignal, secondSignal]];
RACMulticastConnection
該方法旨在解決搏嗡,信號被多次訂閱,信號的block就會多次執(zhí)行的問題
// RACMulticastConnection 其實是一個連接類,可以實現(xiàn)不管訂閱多少次信號,信號的block 都只請求一次
RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"請求數(shù)據(jù)");
[subscriber sendNext:@"數(shù)據(jù)是回來啦"];
return nil;
}];
// 將信號轉(zhuǎn)成連接類
RACMulticastConnection *connection = [signalA publish];
// 訂閱連接類信號
[connection.signal subscribeNext:^(id _Nullable x) {
NSLog(@"第一次連接類信號訂閱=%@", x);
}];
[connection.signal subscribeNext:^(id _Nullable x) {
NSLog(@"第二次連接類信號訂閱=%@", x);
}];
[connection.signal subscribeNext:^(id _Nullable x) {
NSLog(@"第三次連接類信號訂閱=%@", x);
}];
// 連接
[connection connect];
RAC創(chuàng)建一個定時器
該部分主要介紹三個API:
interval
: 每次執(zhí)行之前等待的時間.
scheduler
: 定時器執(zhí)行所在的線程
leeway
: 指的是一個期望的容忍時間,將它設(shè)置為1秒,意味著系統(tǒng)有可能在定時器時間到達(dá)的前1秒或者后1秒才真正觸發(fā)定時器.在調(diào)用時推薦設(shè)置一個合理的 leeway 值,需要注意,就算指定 leeway 值為 0,系統(tǒng)也無法保證完全精確的觸發(fā)時間,只是會盡可能滿足這個需求.
-
[[RACScheduler mainThreadScheduler] afterDelay:@"延時的秒數(shù)" schedule:^{ //延時后執(zhí)行的代碼塊 }];
//延時函數(shù) -
[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * date) { }];
//定時器拉一,每隔多少時間做一次[RACSignal interval:<#(NSTimeInterval)#> onScheduler:<#(RACScheduler *)#>]
[RACScheduler mainThreadScheduler]
//主線程[RACScheduler schedulerWithPriority:(RACSchedulerPriorityHigh) name:@"線程名"]
開辟一個子線程subscribeNext:<#^(id x)nextBlock#>
//signal訂閱信號
-
RAC(label, text) = [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] map:^NSString *(NSDate * date) { return date.description; }];
//秒表采盒,利用map映射方法
將原信號中的內(nèi)容映射成新的指定內(nèi)容。
map映射的理解
RAC中有
RAC中包含兩種映射方法map
蔚润、flattenMap
磅氨,映射方法是將原信號中的內(nèi)容映射成新的指定內(nèi)容。map
的實現(xiàn)方法中可以看出是基于flattenMap
方法的一層封裝
map
:將會創(chuàng)建一個和原來一模一樣的信號嫡纠,只不過新的信號傳遞的值變成了block(value)
flattenMap
:把原信號的內(nèi)容映射成一個新信號烦租,并return返回給一個RACStream類型數(shù)據(jù)。實際上是根據(jù)前一個信號傳遞進來的參數(shù)重新建立了一個信號除盏,這個參數(shù)叉橱,可能會在創(chuàng)建信號的時候用到,也有可能用不到者蠕。
[[textField.rac_textSignal map:^id(id value) {
//在這里對輸入的內(nèi)容做處理窃祝,然后將數(shù)據(jù)返回
return [NSString stringWithFormat:@"map處理后的數(shù)據(jù)%@",value];
}]subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
RACScheduler
RACScheduler就是多線程的RAC封裝,底層是GCD
-
currentScheduler
//獲取當(dāng)前線程 -
mainThreadScheduler
//獲取主線程 -
scheduler
//子線程踱侣,異步的 -
immediateScheduler
//立即執(zhí)行的線程粪小,在主線程中執(zhí)行的
** RACScheduler的優(yōu)先級**
-
+(RACScheduler *)scheduler;
//開辟子線程,默認(rèn)優(yōu)先級和名字 -
+(RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority;
//設(shè)置優(yōu)先級抡句,默認(rèn)名字 -
+(RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name;
//設(shè)置名字和優(yōu)先級
RAC--TextField相關(guān)的API
[[textField rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(id x) {//x==textfield內(nèi)輸入的內(nèi)容 }];
-
[textField.rac_textSignal subscribeNext:^(NSString *x) { }];
以上兩個API可以監(jiān)聽textfield輸入文字變化
RAC-- 添加手勢
[[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) { //手勢執(zhí)行的代碼塊 }];
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
[[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) {
}];
[self.view addGestureRecognizer:tap];
RAC-- Button
-
[ [[button rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(UIButton * btn) { btn.selected = !btn.selected; }];
//button點擊事件 -
RAC(button, backgroundColor) = [RACObserve(button, selected) map:^UIColor *(NSNumber * selected) { return [selected boolValue] ? [UIColor redColor] : [UIColor greenColor]; }];
//利用KVO宏定義檢測按鈕的選中狀態(tài)并修改背景色探膊,button的image同理可做
RAC-- KVO及宏定義
RACObserve宏是KVO在RAC中的實現(xiàn)形式
-
[RACObserve(scrollView, contentOffset) subscribeNext:^(id x) { }];
//監(jiān)控scrolleView的偏移量 -
RACObserve(button, selected)subscribeNext:^(id x) { }];
//監(jiān)控button的選中狀態(tài) -
RAC(button, backgroundColor)
//用于給某個對象的某個屬性綁定。通過RAC宏設(shè)置button的背景色玉转,當(dāng)按鈕狀態(tài)改變時會動態(tài)切換,包括圖片殴蹄、文字等屬性的修改
元組就是OC中的數(shù)組 -
RACTuplePack
:把數(shù)據(jù)包裝成RACTuple(元組類
) -
RACTupleUnpack
:把RACTuple(元組類
)解包成對應(yīng)的數(shù)據(jù)究抓。
RAC-- 代理
對于系統(tǒng)view猾担,可以使用這種方式代替
自定義view,建議直接用RACSubject
#pragma mark 代理
- (void)delegateTest
{
UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"RAC" message:@"ReactiveCocoa" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Ensure", nil];
[[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple * tuple) {
LxDBAnyVar(tuple);
LxDBAnyVar(tuple.first);
LxDBAnyVar(tuple.second);
LxDBAnyVar(tuple.third);
}];
[alertView show];
// 更簡單的方式:
[[alertView rac_buttonClickedSignal]subscribeNext:^(id x) {
LxDBAnyVar(x);
}];
}
RAC-- Notification
-
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"通知名" object:nil] subscribeNext:^(NSNotification * notification) { }];
//RAC的通知要注意有些情況需要自己管理觀察者刺下,具體可以看下面這個大佬寫的
https://blog.csdn.net/jianghui12138/article/details/82466412