RACSignal:
一:創(chuàng)建方法:
+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe;
+ (RACSignal *)error:(NSError *)error;
+ (RACSignal *)return:(ValueType)value;
+ (RACSignal *)empty;
二:操作相關(guān):
1.timeOut:超時,可以讓一個信號在一定的時間后,自動報錯。
[[[RACSignal createSignal:^RACDisposable *(id subscriber) {
????[[RACScheduler mainThreadScheduler] afterDelay:3?schedule:^{
????????[subscriber sendNext:@"rac"];
????????[subscriber sendCompleted];
????}];
????return?nil;
}] timeout:2?onScheduler:[RACScheduler mainThreadScheduler]]
????subscribeNext:^(id x) {
????//TODO
} error:^(NSError *error) {
????//TODO
} completed:^{
????//TODO
}];
2.then:有兩部分?jǐn)?shù)據(jù):想讓上部分先進(jìn)行網(wǎng)絡(luò)請求但是過濾掉數(shù)據(jù),然后進(jìn)行下部分的,拿到下部分?jǐn)?shù)據(jù)?
// 創(chuàng)建信號A?
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {?
????NSLog(@"----發(fā)送上部分請求---");?
????[subscriber sendNext:@"上部分?jǐn)?shù)據(jù)"];?
????[subscriber sendCompleted];?// 必須調(diào)用sendCompleted方法!?
????return?nil;?
}];?
// 創(chuàng)建信號B?
RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {?
????// 發(fā)送請求?
????NSLog(@"--發(fā)送下部分請求--");?
????[subscriber sendNext:@"下部分?jǐn)?shù)據(jù)"];?
????[subscriber sendCompleted];?
????return?nil;?
}];?
// 創(chuàng)建組合信號?
RACSignal *thenSignal = [signalA then:^RACSignal *{?
????return?signalB;?
}];?
// 訂閱信號?
[thenSignal subscribeNext:^(id x) {?
????NSLog(@"%@", x);?
}];
3.zipWith:把兩個信號壓縮成一個信號,只有當(dāng)兩個信號同時發(fā)出信號內(nèi)容時,并且把兩個信號的內(nèi)容合并成一個元組,才會觸發(fā)壓縮流的next事件。
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {
????[subscriber sendNext:@1];
????return?nil;
}];
RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
????[subscriber sendNext:@2];
????return?nil;
}];
// 壓縮信號A,信號B
RACSignal *zipSignal = [signalA zipWith:signalB];
[zipSignal subscribeNext:^(id x) {
????NSLog(@"%@",x);
}];
元組內(nèi)元素的順序不會變,跟發(fā)送的順序無關(guān),而是跟壓縮的順序有關(guān)[signalA zipWith:signalB]---先是A后是B
底層實(shí)現(xiàn):
1.定義壓縮信號,內(nèi)部就會自動訂閱signalA,signalB
2.每當(dāng)signalA或者signalB發(fā)出信號,就會判斷signalA,signalB有沒有發(fā)出個信號,有就會把最近發(fā)出的信號都包裝成元組發(fā)出。
等同于下面的寫法
[[RACSignal combineLatestWith:@[signalA, signalB] subscribeNext:^(id x) {
????//TODO
}];
4.retry重試只要失敗,就會重新執(zhí)行創(chuàng)建信號中的block,直到成功.
__block?int?i =?0;
[[[RACSignal createSignal:^RACDisposable *(id subscriber) {
????if?(i ==?10) {
????????[subscriber sendNext:@1];
????}else{
????????NSLog(@"接收到錯誤");
????????[subscriber sendError:nil];
????}
????i++;
????return?nil;
}] retry] subscribeNext:^(id x) {
????NSLog(@"%@",x);
} error:^(NSError *error) {
}];
5.throttle節(jié)流:在一定時間(1秒)內(nèi),不接收任何信號內(nèi)容,過了這個時間(1秒)獲取最后發(fā)送的信號內(nèi)容發(fā)出
RACSubject *signal = [RACSubject subject];
[[signal throttle:1] subscribeNext:^(id x) {
????NSLog(@"%@",x);
}];
[signal sendNext:@"test"];
6.interval定時:每隔一段時間發(fā)出信號(基本上算是rac定時器了)
[[RACSignal interval:1?onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(id x) {
????NSLog(@"%@",x);
}];
7.delay:延遲發(fā)送
①RACSignal *signal = [[[RACSignal createSignal:^RACDisposable *(id subscriber) {
????[subscriber sendNext:@1];
????return?nil;
}] delay:2] subscribeNext:^(id x) {
????NSLog(@"%@",x);
}];
②[[RACScheduler mainThreadScheduler] afterDelay:2?schedule:^{?
????//TODO?
}];
8.take:從開始一共取N次的信號。takeLast:取最后N次的信號,前提條件,訂閱者必須調(diào)用完成,因?yàn)橹挥型瓿?,就知道總共有多少信?takeUntil:獲取信號直到某個信號執(zhí)行完成
RACSubject *signal = [RACSubject subject];
[[signal take:1] subscribeNext:^(id x) {
????NSLog(@"%@",x);
}];
[signal sendNext:@1];
[signal sendNext:@2];
9.concat:有兩部分?jǐn)?shù)據(jù):想讓上部分先執(zhí)行,完了之后再讓下部分執(zhí)行(都可獲取值), 如果A發(fā)送失敗,B也不會執(zhí)行。A和B是依賴關(guān)系
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {?
????[subscriber sendNext:@"上部分?jǐn)?shù)據(jù)"];?
????[subscriber sendCompleted];?// 必須要調(diào)用sendCompleted方法!?
????return?nil;?
}];?
RACSignal *signalsB = [RACSignal createSignal:^RACDisposable *(id subscriber) {?
????[subscriber sendNext:@"下部分?jǐn)?shù)據(jù)"];?
????return?nil;?
}];?
// concat:按順序去鏈接?
RACSignal *concatSignal = [signalA concat:signalsB];?
// 訂閱組合信號?
[concatSignal subscribeNext:^(id x) {?
????NSLog(@"%@",x);?
}];
10.RAC線程切換
RACScheduler *backgroundScheduler = [RACScheduler scheduler];
RACSignal *testSignal = [[RACSignal
createSignal:^RACDisposable *(id subscriber) {
????// 這段代碼會運(yùn)行在子線程
????[subscriber sendNext:@1];
????[subscriber sendCompleted];
????return?nil;
}]
subscribeOn:backgroundScheduler];
[[testSignal deliverOn: [RACScheduler mainThreadScheduler]] subscribeNext:^(id _Nullable x) {
????NSLog(@"在主線程執(zhí)行");
}];
takeLast表示倒數(shù)的前兩次
接下來是幾個block回調(diào)方法
takeWhileBlock BOOL值,意思是當(dāng)返回YES的時候,訂閱者才能收到信號
skipWhileBlock BOOL值,意思是當(dāng)返回YES的時候,訂閱者就會跳過信號,NO的時候才接受
skipUntilBlock BOOL值,意思是 返回NO的時候,不會收到消息, 直到返回YES的時候才開始收消息。
distinctUntilChanged 表示兩個消息相同的時候,只會發(fā)送一個請求
11.merge: 把多個信號合并為一個信號,任何一個信號有新值的時候就會調(diào)用
RACSubject *signalA = [RACSubject subject];
RACSubject *signalB = [RACSubject subject];
RACSignal *signals = [signalA merge:signalB];
[signals subscribeNext:^(id x) {
????NSLog(@"%@",x);
}];
[signalA sendNext:@1];
[signalB sendNext:@2];
[signalB sendNext:@3];
12.combineLatest: reduce:把validUsernameSignal和validPasswordSignal產(chǎn)生的最新的值聚合在一起,并生成一個新的信號。每次這兩個源信號的任何一個產(chǎn)生新值時,reduce block都會執(zhí)行,block的返回值會發(fā)給下一個信號
RACSignal *signUpActiveSignal =
[RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal]
reduce:^id(NSNumber *usernameValid, NSNumber *passwordValid) {
????return?@([usernameValid boolValue] && [passwordValid boolValue]);
}];
combineLatest:將多個信號合并起來,并且拿到各個信號的最新的值,必須每個合并的signal至少都有過一次sendNext,才會觸發(fā)合并的信號。
reduce聚合:用于信號發(fā)出的內(nèi)容是元組,把信號發(fā)出元組的值聚合成一個值。reduceblcok中的參數(shù),有多少信號組合,reduceblcok就有多少參數(shù),每個參數(shù)就是之前信號發(fā)出的內(nèi)容
reduceblcok的返回值:聚合信號之后的內(nèi)容。
rac_liftSelector:withSignals 也是類似,當(dāng)signalA和signalB都至少sendNext過一次,接下來只要其中任意一個signal有了新的內(nèi)容,相應(yīng)方法就會自動被調(diào)用。
13.skip:表示輸入第幾次,不會被監(jiān)聽到,跳過第幾次發(fā)出的信號
[[_textField.rac_textSignal skip:1] subscribeNext:^(id x) {
????NSLog(@"%@",x);
}];
14.distinctUntilChanged:過濾,當(dāng)上一次和當(dāng)前的值不一樣,就會發(fā)出內(nèi)容
[[_textField.rac_textSignal distinctUntilChanged] subscribeNext:^(id x) {
????NSLog(@"%@",x);
}];
15.filter:過濾信號,使用它可以獲取滿足條件的信號
[_textField.rac_textSignal filter:^BOOL(NSString *value) {
????return?value.length >?3;
}];
16.ignore:忽略某些值的信號
[[_textField.rac_textSignal ignore:@"1"] subscribeNext:^(id x) {
????NSLog(@"%@",x);
}];
17.startWith:startWith:@"123"等同于[subscriber sendNext:@"123"] 也就是第一個發(fā)送
RACSignal * signal = [[RACSignal createSignal:^RACDisposable *(id subscriber) {
????//[subscriber sendNext:@"123"];//startWith:@"123"等同于這句話
????[subscriber sendNext:@"rac"];
????[subscriber sendCompleted];
????return?nil;
}] startWith:@"123"];
[signal subscribeNext:^(id x) {
}];
18.switchToLatest:只能用于signalOfSignals(信號的信號)
RACSubject *signalOfSignals = [RACSubject subject];
RACSubject *signal = [RACSubject subject];
[signalOfSignals.switchToLatest subscribeNext:^(id x) {
????NSLog(@"%@",x);
}];
[signalOfSignals sendNext:signal];
[signal sendNext:@1];
19.
doNext: 執(zhí)行Next之前,會先執(zhí)行這個Block
doCompleted: 執(zhí)行sendCompleted之前,會先執(zhí)行這個Block
[[[[RACSignal createSignal:^RACDisposable *(id subscriber) {
????[subscriber sendNext:@1];
????[subscriber sendCompleted];
????return?nil;
}] doNext:^(id x) {
????// 執(zhí)行[subscriber sendNext:@1];之前會調(diào)用這個Block
}] doCompleted:^{
????// 執(zhí)行[subscriber sendCompleted];之前會調(diào)用這個Block
}]];
一個頁面多個網(wǎng)絡(luò)請求:
①rac_liftSelector:withSignalsFromArray:Signals:當(dāng)傳入的Signals(信號數(shù)組),每一個signal都至少sendNext過一次,就會去觸發(fā)第一個selector參數(shù)的方法
RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id subscriber) {?
????[subscriber sendNext:@"發(fā)送請求1"];?
????return?nil;?
}];?
RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id subscriber) {?
????[subscriber sendNext:@"發(fā)送請求2"];?
????return?nil;?
}];?
// 使用注意:幾個信號,參數(shù)一的方法就幾個參數(shù),每個參數(shù)對應(yīng)信號發(fā)出的數(shù)據(jù)。?
[self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[request1,request2]];
②接口串聯(lián)?
@weakify(self)
RACSignal *finalSignal = [[self fetchData]
flattenMap:^RACSignal *(NSString *dataResult) {
????@strongify(self)
????return?[self fetchData2:dataResult];
}];
RACCommand篇:
一般用于:1:button點(diǎn)擊 2:包裝網(wǎng)絡(luò)請求接口
一: RACCommand的創(chuàng)建有兩種形式:
- (id)initWithSignalBlock:(RACSignal * (^)(id input))signalBlock;?
- (id)initWithEnabled:(RACSignal *)enabledSignal signalBlock:(RACSignal * (^)(id input))signalBlock;
注意:
1.伴隨著command一起構(gòu)建的signal,記得要在操作完成后發(fā)送完成消息以表示其執(zhí)行完了,[subscriber sendCompleted];否則不能再執(zhí)行此command。
2.UIButton中有屬性rac_command用于綁定一個已經(jīng)創(chuàng)建好的command,當(dāng)使用第二種方式創(chuàng)建command時,button的enable屬性會隨command的可執(zhí)行性而改變,意思是當(dāng)傳遞布爾事件的信號傳遞了真值事件,按鈕才可使用。另外,當(dāng)你按下按鈕,command開始執(zhí)行時,按鈕的enable被自動設(shè)置成了NO,除非command執(zhí)行完了。
3.當(dāng)button的rac_command已經(jīng)綁定了某個command,而這個command又是以第二種方式初始化,那么你就不能動態(tài)改變button的enable,如:RAC(self.button, enable) = someSignal;這樣子運(yùn)行起來會報錯。
二:執(zhí)行RACCommand:
- (RACSignal *)execute:(id)input;
三:訂閱RACCommand:
[[[command executionSignals] switchToLatest]
subscribeNext:^(id x) {
????// TODO
}];
四:在對command進(jìn)行錯誤處理的時候,不使用subscribeError:對command的executionSignals進(jìn)行錯誤的訂閱,executionSignals這個信號是不會發(fā)送error事件的,當(dāng)command包裹的信號發(fā)送error事件時,用到command的一個屬性:errors,可以對錯誤進(jìn)行訂閱:
[command.errors
subscribeNext:^(NSError *x) {
????//TODO
}];
也可以通過executing屬性判斷是否正在執(zhí)行
注意事項(xiàng)篇:
1.RACSignal *signalReplay = signal.replay;?
實(shí)現(xiàn)多個訂閱者沒有副作用的效果?
2.RACSubject和RACReplaySubject的區(qū)別
RACSubject必須要先訂閱信號之后才能發(fā)送信號,而RACReplaySubject可以先發(fā)送信號后訂閱
3.避免副作用效果處理(RACMulticastConnection)
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {?
????NSLog(@"發(fā)送請求");?
????[subscriber sendNext:@"ws"];?
????return?nil;?
}];?
RACMulticastConnection *connection = [signal publish];?
[connection.signal subscribeNext:^(id x) {?
????NSLog(@"%@", x);?
}];?
[connection.signal subscribeNext:^(id x) {?
????NSLog(@"%@", x);?
}];?
[connection.signal subscribeNext:^(id x) {?
????NSLog(@"%@", x);?
}];?
[connection connect];
4.RACObserve默認(rèn)會含有self,所以在RAC block內(nèi)使用RACObserve時一定要用@weakify(self);@strongify(self);處理,否則會導(dǎo)致內(nèi)存泄漏
5.RACSubject被map之后 在發(fā)送next信號時一定要發(fā)送complete方法,否則會導(dǎo)致內(nèi)存泄漏。所以不管任何場景下都要sendComplete/sendError
6.ViewModel里用來保存數(shù)據(jù)的數(shù)組,不能使用NSMutableArray。原因是RAC是基于KVO的,而NSMutableArray的Add和Remove方法并不會給KVO發(fā)送通知,因此對NSMutableArray進(jìn)行RACObserve時,并不會達(dá)到我們想要的結(jié)果。(同理其他Mutable的也都不能用)
7.flattenmap與map的區(qū)別是: block中返回的東西不同,flattenmap返回的是signal信號。flattenMap方法,實(shí)際上是根據(jù)前一個信號傳遞進(jìn)來的參數(shù)重新建立了一個信號,這個參數(shù),可能會在創(chuàng)建信號的時候用到,也有可能根本用不到。
8.RACScheduler RAC中的隊(duì)列,用GCD封裝的
9.RACBehaviorSubject最重要的特性就是在訂閱時,向最新的訂閱者發(fā)送之前的消息,RACReplaySubject 相當(dāng)于一個自帶 buffer 的 RACBehaviorSubject,它可以在每次有新的訂閱者訂閱之后發(fā)送之前的全部消息。
10.deliverOn: 內(nèi)容傳遞切換到制定線程中,副作用在原來線程中。
subscribeOn: 內(nèi)容傳遞和副作用都會切換到制定線程中。
11.熱信號是主動的,即使你沒有訂閱事件,它仍然會時刻推送。而冷信號是被動的,只有當(dāng)你訂閱的時候,它才會發(fā)送消息。熱信號可以有多個訂閱者,是一對多,而冷信號只能一對一,當(dāng)有不同的訂閱者,消息會從新完整發(fā)送。
替換原生方法:
1.NSData
rac_readContentsOfURL: options: scheduler: 比oc多出線程設(shè)置
2.NSDictionary
rac_keySequence key 集合
rac_valueSequence value 集合
3.RACTuple元組
RACTuple *tuple = RACTuplePack(@1,@2,@4);?
// 宏的參數(shù)類型要和元組中元素類型一致, 右邊為要解析的元組。?
RACTupleUnpack_(NSNumber *num1, NSNumber *num2, NSNumber * num3) = tuple;?
NSLog(@"%@ %@ %@", num1, num2, num3);
4.代理?
①之前都是需要通過代理監(jiān)聽,給紅色View添加一個代理屬性,點(diǎn)擊按鈕的時候,通知代理做事情?
rac_signalForSelector:把調(diào)用某個對象的方法的信息轉(zhuǎn)換成信號,就要調(diào)用這個方法,就會發(fā)送信號。?
這里表示只要redV調(diào)用btnClick:就會發(fā)出信號,訂閱就好了。
[[redV rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {?
????NSLog(@"點(diǎn)擊紅色按鈕");?
}];
②
[[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple * tuple) {
????//TODO
}];
eg:
@weakify(self)
self.proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UITextFieldDelegate)];
[[self.proxy rac_signalForSelector:@selector(textFieldShouldReturn:)]
subscribeNext:^(id x) {
????@strongify(self)
}];
self.nameText.delegate = (id)self.proxy;
5.KVO
①
[[redV rac_valuesAndChangesForKeyPath:@"center"?options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {?
????NSLog(@"%@",x);?
}];
②RACObserve(<#TARGET#>, <#KEYPATH#>)
6.監(jiān)聽事件
[[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {?
????NSLog(@"按鈕被點(diǎn)擊了");?
}];
7.通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {?
????NSLog(@"鍵盤彈出");?
}];
8.手勢
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] init];
[[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) {
????//TODO
}];
[self.view addGestureRecognizer:tap];
9.遍歷數(shù)組
NSArray *numbers = @[@1,@2,@3,@4];
[numbers.rac_sequence.signal subscribeNext:^(id x) {
????NSLog(@"%@",x);
}];
10.遍歷字典,遍歷出來的鍵值對會包裝成RACTuple
NSDictionary *dict = @{@"name":@"xmg",@"age":@18};
[dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
????// 解包元組,會把元組的值,按順序給參數(shù)里面的變量賦值
????RACTupleUnpack(NSString *key,NSString *value) = x;
????// 相當(dāng)于以下寫法
????// NSString *key = x[0];
????//NSString *value = x[1];
????NSLog(@"%@ %@",key,value);
}];
雙向綁定:
RAC(<#TARGET, ...#>)//單向綁定
RACChannelTo(<#TARGET, ...#>)//雙向綁定
RACChannelTo(self, filmType, @(ASHRecipeFilmTypeColourNegative)) 第三個參數(shù)是指,如果值的變化中出現(xiàn) nil,那么就會使用這個值來代替,相當(dāng)于一個默認(rèn)值。
RACChannelTo用的是RACKVOChannel實(shí)現(xiàn)的
eg:
RACChannelTo(self.someLabel, text) = RACChannelTo(self.viewModel, someProperty);
[self.textField.rac_newTextChannel subscribe:self.viewModel.someChannel];
[self.viewModel.someChannel subscribe:self.textField.rac_newTextChannel];
RACChannelTo(self, reviewID) = self.viewModel.someChannel;
[self.textField.rac_newTextChannel subscribe:self.anotherTextField.rac_newTextChannel];
[self.anotherTextField.rac_newTextChannel subscribe:self.textField.rac_newTextChannel];
[[RACKVOChannel alloc] initWithTarget:view keyPath:@"property"?nilValue:nil][@"followingTerminal"]?
= [[RACKVOChannel alloc] initWithTarget:model keyPath:@"property"?nilValue:nil][@"followingTerminal"];
與RACChannelTo(view, property) = RACChannelTo(model, property);等價