繼續(xù)完成RAC的坑
前言
經(jīng)過(guò)上篇文章的學(xué)習(xí),相信不少讀者已經(jīng)熟悉了RAC的基本用法,可以嘗試在以后的開發(fā)中不用add target
語(yǔ)句了。這篇文章將稍微深入一點(diǎn)了解RAC的更多功能以及適用場(chǎng)合。
什么是信號(hào)
RAC的核心就是信號(hào),即RACSignal
。
信號(hào)可以看做是傳遞信號(hào)的工具,當(dāng)數(shù)據(jù)變化時(shí),信號(hào)就會(huì)發(fā)送改變的信息,以通知信號(hào)的訂閱者執(zhí)行方法。
熱/冷信號(hào)
默認(rèn)一個(gè)信號(hào)都是冷信號(hào),也就是值改變了,也不會(huì)觸發(fā),只有訂閱了這個(gè)信號(hào),這個(gè)信號(hào)才會(huì)變?yōu)闊嵝盘?hào),值改變了才會(huì)觸發(fā)。
自己動(dòng)手寫一個(gè)RACSignal
//創(chuàng)建信號(hào)
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"signal"];
[subscriber sendCompleted];
return nil;
}];
//訂閱信號(hào)
[signal subscribeNext:^(id x) {
NSLog(@"x = %@", x);
} error:^(NSError *error) {
NSLog(@"error = %@", error);
} completed:^{
NSLog(@"completed");
}];
這是一個(gè)信號(hào)從創(chuàng)建到接收的完整過(guò)程,我們接下來(lái)看看控制臺(tái)輸出了什么。
2016-01-05 14:33:06.106 RACStudyTest[895:38671] x = signal
2016-01-05 14:33:06.106 RACStudyTest[895:38671] completed
可以看到,創(chuàng)建信號(hào)時(shí)我們sent了一個(gè)signal,在我們訂閱subscribeNext
時(shí)存儲(chǔ)在x中的就是這個(gè)字符串signal。從這里看出來(lái),不但我們可以給訂閱者傳遞字符串,只要是一個(gè)類一個(gè)對(duì)象我們都可以傳遞。
另一方面控制臺(tái)輸出了completed說(shuō)明訂閱信號(hào)部分的completed塊下得方法也被執(zhí)行了,這是因?yàn)轲I哦在創(chuàng)建時(shí)發(fā)送完signal后又發(fā)送了一個(gè)completed。同理,error下得方法我們也可以這樣調(diào)用。
融匯貫通一下,我們之前學(xué)的所有RAC的用法就是一個(gè)創(chuàng)建信號(hào)訂閱信號(hào)的過(guò)程!
信號(hào)的處理
map
這里的map不是地圖,而是映射的意思,就是創(chuàng)建一個(gè)訂閱者的映射并且返回?cái)?shù)據(jù),具體用法我們來(lái)看代碼。
[[self.textFild.rac_textSignal map:^id(id value) {
NSLog(@"%@", value);
return @1;
}] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
在TextFild控件中輸入good,輸出:
2016-01-05 15:02:15.180 RACStudyTest[1418:57245] 1
2016-01-05 15:02:21.710 RACStudyTest[1418:57245] g
2016-01-05 15:02:21.711 RACStudyTest[1418:57245] 1
2016-01-05 15:02:22.954 RACStudyTest[1418:57245] go
2016-01-05 15:02:22.955 RACStudyTest[1418:57245] 1
2016-01-05 15:02:23.464 RACStudyTest[1418:57245] goo
2016-01-05 15:02:23.464 RACStudyTest[1418:57245] 1
2016-01-05 15:02:23.705 RACStudyTest[1418:57245] good
2016-01-05 15:02:23.705 RACStudyTest[1418:57245] 1
還是之前那個(gè)監(jiān)聽textfild編輯變化的例子,可以看到,當(dāng)信號(hào)被訂閱變成熱信號(hào)后,這里的map構(gòu)造的映射塊value的值就是控件中的字符變化,而訂閱者x的值就是映射者的返回值1。
根據(jù)這個(gè)功能我們就可以對(duì)我們監(jiān)測(cè)的東西和我們需求的東西進(jìn)行轉(zhuǎn)換。比如監(jiān)聽了字符串變化,我們需要的時(shí)變化后的字符串長(zhǎng)度而不是變化的字符串本身,則可以在map的返回值中返回text.length
,就可以實(shí)時(shí)捕獲到字符串長(zhǎng)度;甚至做一個(gè)映射表,將各個(gè)變化進(jìn)行一對(duì)一或者一對(duì)多或者多對(duì)一的處理。
filter
filter就是過(guò)濾,它可以幫助你篩選出你需要的信號(hào)變化。
[[self.textFild.rac_textSignal filter:^BOOL(NSString *value) {
return [value length] > 3;
}] subscribeNext:^(id x) {
NSLog(@"x = %@", x);
}];
輸入goodnight,輸出:
2016-01-05 15:18:20.492 RACStudyTest[1490:66721] x = good
2016-01-05 15:18:27.917 RACStudyTest[1490:66721] x = goodn
2016-01-05 15:18:28.129 RACStudyTest[1490:66721] x = goodni
2016-01-05 15:18:28.433 RACStudyTest[1490:66721] x = goodnig
2016-01-05 15:18:28.930 RACStudyTest[1490:66721] x = goodnigh
2016-01-05 15:18:29.155 RACStudyTest[1490:66721] x = goodnight
上述例子是在字符串長(zhǎng)度大于3時(shí)才會(huì)輸出變換后的字符串。
take/skip/repeat
take是獲取,skip是跳過(guò),這兩個(gè)方法后面跟著的都是NSInteger。所以take 2
就是獲取前兩個(gè)信號(hào),skip 2
就是跳過(guò)前兩個(gè)。repeat是重復(fù)發(fā)送信號(hào)。
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1"];
[subscriber sendNext:@"2"];
[subscriber sendNext:@"3"];
[subscriber sendNext:@"4"];
[subscriber sendNext:@"5"];
[subscriber sendCompleted];
return nil;
}] take:2];
[signal subscribeNext:^(id x) {
NSLog(@"%@", x);
}completed:^{
NSLog(@"completed");
}];
這個(gè)demo只會(huì)輸出前兩個(gè)信號(hào)1和2還有完成信號(hào)completed,skip,repeat同理。
相似的還有takeLast
takeUntil
takeWhileBlock
skipWhileBlock
skipUntilBlock
repeatWhileBlock
都可以根據(jù)字面意思來(lái)理解。
delay
延時(shí)信號(hào),顧名思義,即延遲發(fā)送信號(hào)。
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"delay"];
[subscriber sendCompleted];
return nil;
}] delay:2];
NSLog(@"tag");
[signal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
輸出:
2016-01-05 15:27:55.908 RACStudyTest[1543:71256] tag
2016-01-05 15:27:58.102 RACStudyTest[1543:71256] delay
看時(shí)間可以發(fā)現(xiàn)訂閱者延遲了2秒才收到信號(hào)打印出x的值。PS:還有0.2s誤差是因?yàn)檫\(yùn)行到不同代碼行的時(shí)間差
throttle
節(jié)流,在我們做搜索框的時(shí)候,有時(shí)候需求的時(shí)實(shí)時(shí)搜索,即用戶每每輸入字符,view都要求展現(xiàn)搜索結(jié)果。這時(shí)如果用戶搜索的字符串較長(zhǎng),那么由于網(wǎng)絡(luò)請(qǐng)求的延時(shí)可能造成UI顯示錯(cuò)誤,并且多次不必要的請(qǐng)求還會(huì)加大服務(wù)器的壓力,這顯然是不合理的,此時(shí)我們就需要用到節(jié)流。
[[[self.textFild rac_textSignal] throttle:0.5] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
加了節(jié)流管道,后面跟上了類型為NSTimeInterval的參數(shù)后,只有0.5S內(nèi)信號(hào)不產(chǎn)生變化才會(huì)發(fā)送請(qǐng)求,這樣快速的輸入也不會(huì)造成多次輸出。
distinctUntilChanged
網(wǎng)絡(luò)請(qǐng)求中為了減輕服務(wù)器壓力,無(wú)用的請(qǐng)求我們應(yīng)該盡可能不發(fā)送。distinctUntilChanged
的作用是使RAC不會(huì)連續(xù)發(fā)送兩次相同的信號(hào),這樣就解決了這個(gè)問(wèn)題。
[[[self.textFild rac_textSignal] distinctUntilChanged] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
timeout
超時(shí)信號(hào),當(dāng)超出限定時(shí)間后會(huì)給訂閱者發(fā)送error信號(hào)。
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{
[subscriber sendNext:@"delay"];
[subscriber sendCompleted];
}];
return nil;
}] timeout:2 onScheduler:[RACScheduler mainThreadScheduler]];
[signal subscribeNext:^(id x) {
NSLog(@"%@", x);
} error:^(NSError *error) {
NSLog(@"%@", error);
}];
輸出:
2016-01-05 15:41:58.686 RACStudyTest[1604:76716] Error Domain=RACSignalErrorDomain Code=1 "The operation couldn’t be completed. (RACSignalErrorDomain error 1.)"
由于在創(chuàng)建信號(hào)是限定了延遲3秒發(fā)送,但是加了timeout2秒的限定,所以這一定是一個(gè)超時(shí)信號(hào)。這個(gè)信號(hào)被訂閱后,由于超時(shí),不會(huì)執(zhí)行訂閱成功的輸出x方法,而是跳到error的塊輸出了錯(cuò)誤信息。timeout在用RAC封裝網(wǎng)絡(luò)請(qǐng)求時(shí)可以節(jié)省不少的代碼量。
ignore
忽略信號(hào),指定一個(gè)任意類型的量(可以是字符串,數(shù)組等),當(dāng)需要發(fā)送信號(hào)時(shí)講進(jìn)行判斷,若相同則該信號(hào)會(huì)被忽略發(fā)送。
[[[self.textFild rac_textSignal] ignore:@"good"] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
總結(jié)
這篇文章比上一篇稍稍深入了RAC的附加功能,但還有很多例如扁平映射flattenMap
,Signal of Signals都沒有提到,RAC的東西實(shí)在太多太多,我說(shuō)的也僅僅是九牛一毛。我將繼續(xù)研究RAC的深入用法,并發(fā)文給大家介紹。如果文中有什么錯(cuò)誤,也希望大家給我指出來(lái),我將萬(wàn)分感謝。也歡迎來(lái)我的博客看看。