RAC使用函數(shù)響應(yīng)式編程的思想,將我們平時用到的按鈕點擊事件、KVO、代理、通知等等封裝起來,處理業(yè)務(wù)邏輯的代碼放到一起,使代碼更加的簡潔、高內(nèi)聚、低耦合,那么我們來看看它的具體使用。
一、RAC的基本使用
RAC和KVO
//使用RAC來監(jiān)聽字符串myString的變化
[RACObserve(self, myString) subscribeNext:^(id _Nullable x) {
NSLog(@"myString值發(fā)生變化后的新值:%@",x);
}];
一句代碼就可以代替KVO,代碼和功能在一起,寫起來方便,簡單易讀。
-
RACObserve(TARGET, KEYPATH):RAC提供的宏定義,用來監(jiān)聽值的變化,返回RACSignal類型的信號,有了這個宏定義返回的信號,就可以訂閱該信號,變化后的值。 -
subscribeNext::訂閱信號,只有訂閱了信號才可以收到值的變化。
RAC和通知
//監(jiān)聽鍵盤彈出的通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
//處理接收到的通知
NSLog(@"%@",x);
}];
rac_addObserverForName: object::也可以添加開發(fā)中自己注冊的通知,添加監(jiān)聽通知的方法,寫在NSNotificationCenter分類中。RAC中有很多的分類實現(xiàn)不同的方法。
RAC和代理
//添加UITextFieldDelegate代理方法textFieldDidBeginEditing:信號
[[self rac_signalForSelector:@selector(textFieldDidBeginEditing:) fromProtocol:@protocol(UITextFieldDelegate)] subscribeNext:^(RACTuple * _Nullable x) {
//RACTuple:元組類型
NSLog(@"%@",x);
}];
//實現(xiàn)代理
self.textField.delegate = self;
rac_signalForSelector:fromProtocol:添加代理方法信號。
- 值得注意的是,接收訂閱信號的參數(shù)是
RACTuple元組類型,熟悉swift語法的話對元組類型一定不陌生,元組是把多個值組合成一個復(fù)合值。元組內(nèi)的值可以是任意類型,并不要求是相同類型。以下是上面代碼接受到信號的打印信息,可以看一下元組是什么樣子的:
<RACTuple: 0x6000018a5960> (
<UITextField: 0x7f966fd6b3d0; frame = (72 219; 270 34); text = ''; opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x60000143dce0>; layer = <CALayer: 0x600001ad9240>>,
)
UIButton
//按鈕點擊信號
@weakify(self);//防止循環(huán)引用的問題
[[self.myButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
@strongify(self);//暫時強引用
//接收到點擊信號的響應(yīng),
if(self.textField.text.length == 0) {
NSLog(@"請先輸入名稱");
}else {
//...
}
}];
rac_signalForControlEvents ::UIButton的分類方法,產(chǎn)生點擊信號。
- 在訂閱信號中進行
UI的綁定,這種思想在MVVM+RAC的架構(gòu)中非常重要,MVVM中通過block回調(diào)可以完成ViewModel向UI層數(shù)據(jù)傳遞,使用rac進行UI綁定,就可以完成UI層向ViewModel層的數(shù)據(jù)傳輸,從而實現(xiàn)雙向綁定。
UITextField
//輸入框文字改變信號
[self.textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
//打印當(dāng)前textField的text
NSLog(@"%@",x);
}];
rac_textSignal:輸入框輸入的文字發(fā)生改變的信號,訂閱這個信號可以處理文字長度,輸入格式等。
手勢
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
self.label.userInteractionEnabled = YES;
[self.label addGestureRecognizer:tap];
[tap.rac_gestureSignal subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
NSLog(@"%@",x);
}];
rac_gestureSignal:手勢觸發(fā)信號。
序列 sequence
數(shù)組遍歷
NSArray *nameArray = @[@"Niki",@"Pan",@"Ding",@"Jiao"];
//數(shù)組遍歷
[nameArray.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);//依次打印數(shù)組元素
}];
NSDictionary *dict = @{@"name":@"Niki",@"age":@"18",@"number":@"999"};
//遍歷字典
[dict.rac_sequence.signal subscribeNext:^(id _Nullable x) {
//元組類型
NSLog(@"%@",x);
//RACTwoTuple繼承自RACTuple,表示有兩個值的元組
RACTwoTuple *tuple = (RACTwoTuple *)x;
NSLog(@"key == %@ , value = %@",tuple[0],tuple[1]);
}];
集合類型在RAC中都封裝成了序列,通過訪問rac_sequence屬性得到序列。
二、RAC中的高階函數(shù)
-
信號映射:
map和flattenMap,把原有信號中的值映射成新的值。
-
map:方法返回依舊是一個信號RACSignal類型,如下代碼,將rac_textSignal信號中的字符串經(jīng)過map映射處理成字符串的長度。注意:map的block中必須要返回對象類型的數(shù)據(jù)。
[[self.passField.rac_textSignal map:^id _Nullable(NSString * _Nullable value) {
return @(value.length);
}] subscribeNext:^(id _Nullable x) {
NSLog(@"映射后的新值:%@",x);//輸出字符串長度
}];
-
flattenMap:把源信號的內(nèi)容映射成一個新的信號,信號可以是任意類型。
[[self.passField.rac_textSignal flattenMap:^__kindof RACSignal * _Nullable(NSString * _Nullable value) {
//返回RACSignal類型
return [RACReturnSignal return:@(value.length)];
}] subscribeNext:^(id _Nullable x) {
NSLog(@"映射后的新值:%@",x);
}];
-
信號過濾:
filter,ignore,distinctUntilChanged
-
filter:獲取滿足條件的信號 -
ignore:內(nèi)部調(diào)用了filter,忽略掉ignore傳入的參數(shù)。 -
distinctUntilChanged:跟函數(shù)名字面意思一樣,直到值和上一次有區(qū)別才會發(fā)出信號,忽略的是和上一次值一樣的信號。經(jīng)常用于UI的刷新,值發(fā)生變化的時候才刷新。
下面依次來看代碼示例:
//filter
[[self.passField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
//輸入字符串長度大于3
return value.length > 3;
}] subscribeNext:^(id _Nullable x) {
//獲取到輸入字符串長度大于3的信號
//當(dāng)輸入字符串長度大于3才會在這里打印
NSLog(@"%@",x);
}];
//ignore
[[self.passField.rac_textSignal ignore:@"n"] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"%@",x);//輸入n,這里收不到信號
}];
//distinctUntilChanged
RACSubject *subject = [RACSubject subject];
[[subject distinctUntilChanged] subscribeNext:^(id x) {
NSLog(@"%@", x);//收到的信號依次是a b c,第三次發(fā)送的b被忽略了
}];
// 發(fā)送信號
[subject sendNext:@"a"];
[subject sendNext:@"b"];
[subject sendNext:@"b"];
[subject sendNext:@"c"];
-
信號合并:
combineLatest,reduce,merge,zipWith
-
combineLatest:將多個信號合并起來,并且拿到各個信號的最新值,必須每個合并的signal至少都有過一次sendNext,才會觸發(fā)合并的信號。 -
reduce:把combineLatest的元組聚合成一個值發(fā)送出去,相當(dāng)于對combineLatest的進一步加工,必須每個合并的signal至少都有過一次sendNext,才會觸發(fā)合并的信號。 -
merge:把多個信號合并成一個信號,任何一個信號發(fā)送數(shù)據(jù),都能監(jiān)聽到。 -
zip:把最近發(fā)出的信號包裝成元組發(fā)出。當(dāng)每個信號同事發(fā)出信號內(nèi)容,才會發(fā)出信號。
//combineLatest
//創(chuàng)建三個信號
RACSubject *s1 = [RACSubject subject];
RACSubject *s2 = [RACSubject subject];
RACSubject *s3 = [RACSubject subject];
//合并三個信號
RACSignal *combineSg = [RACSignal combineLatest:@[s1,s2,s3]];
//訂閱合并信號
[combineSg subscribeNext:^(id _Nullable x) {
//當(dāng)三個信號都有一次發(fā)送的時候才能收到聚合信號
//這里收到是一個元組類型,三個元素分別為三個信號的最新值
NSLog(@"%@",x);
}];
//發(fā)送信號
[s1 sendNext:@"a"];
[s2 sendNext:@"1"];
[s3 sendNext:@"!"];
[s1 sendNext:@"b"];
[s2 sendNext:@"2"];
[s3 sendNext:@"@"];
[s1 sendNext:@"c"];
[s2 sendNext:@"3"];
[s3 sendNext:@"#"];
//reduce
RACSubject *s1 = [RACSubject subject];
RACSubject *s2 = [RACSubject subject];
RACSubject *s3 = [RACSubject subject];
RACSignal *reduceSg = [RACSignal combineLatest:@[s1,s2,s3] reduce:^(id value1,id value2,id value3){
//value1 value2 value3為三個信號的最新值
return [NSString stringWithFormat:@"%@,%@,%@",value1,value2,value3];
}];
//訂閱聚合信號
[reduceSg subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
//發(fā)送信號
[s1 sendNext:@"a"];
[s2 sendNext:@"1"];
[s3 sendNext:@"!"];
[s1 sendNext:@"b"];
[s2 sendNext:@"2"];
[s3 sendNext:@"@"];
[s1 sendNext:@"c"];
[s2 sendNext:@"3"];
[s3 sendNext:@"#"];
/*打印輸出結(jié)果,三個信號都發(fā)送一次sendNext之后才會收到第一次聚合信號
a,1,!
b,1,!
b,2,!
b,2,@
c,2,@
c,3,@
c,3,#
*/
//merge
RACSubject *s1 = [RACSubject subject];
RACSubject *s2 = [RACSubject subject];
RACSubject *s3 = [RACSubject subject];
//合并三個信號
RACSignal *mergeSg = [RACSignal merge:@[s1,s2,s3]];
[mergeSg subscribeNext:^(id _Nullable x) {
//收到九次信號,每次打印當(dāng)前信號最新值
NSLog(@"%@",x);
}];
[s1 sendNext:@"a"];
[s2 sendNext:@"1"];
[s3 sendNext:@"!"];
[s1 sendNext:@"b"];
[s2 sendNext:@"2"];
[s3 sendNext:@"@"];
[s1 sendNext:@"c"];
[s2 sendNext:@"3"];
[s3 sendNext:@"#"];
//zip
RACSubject *s1 = [RACSubject subject];
RACSubject *s2 = [RACSubject subject];
RACSubject *s3 = [RACSubject subject];
RACSignal *zipSg = [RACSignal zip:@[s1,s2,s3]];
[zipSg subscribeNext:^(id _Nullable x) {
/*收到兩次信號
( a, 1, !, ) (b, 3, @, )
*/
NSLog(@"%@",x);
}];
[s1 sendNext:@"a"];
[s2 sendNext:@"1"];
[s3 sendNext:@"!"];
[s1 sendNext:@"b"];
// [s2 sendNext:@"2"];
[s3 sendNext:@"@"];
[s1 sendNext:@"c"];
[s2 sendNext:@"3"];
[s3 sendNext:@"#"];
-
信號連接:
concat,then
-
concat:信號連接起來,按順序響應(yīng)信號。 -
then:只收到then返回的信號,之前的信號會被忽略
//concat
RACSignal *s1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"信號1"];
[subscriber sendCompleted];
return nil;
}];
RACSignal *s2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"信號2"];
[subscriber sendCompleted];
return nil;
}];
RACSignal *contactSg = [s1 concat:s2];
[contactSg subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);//依次打印 信號1 信號2
}];
//then
RACSignal *s1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"信號1"];
[subscriber sendCompleted];
return nil;
}];
RACSignal *s2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"信號2"];
[subscriber sendCompleted];
return nil;
}];
[[s2 then:^RACSignal * _Nonnull{
return s1;
}] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);//只收到信號1,第一個信號會被忽略
}];
-
信號操作時間:
timeout,interval,dely
-
timeout:超時信號,超過設(shè)定時間后會收到錯誤信號 -
dely:延時發(fā)送信號
//設(shè)置signal的超時時間是2秒
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
//延時三秒發(fā)送信號
[[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);//2秒后打印信息,因為發(fā)送信號延時了3秒
}];
- 還有其他的,持續(xù)更新。
RAC信號響應(yīng)流程
- 信號聲命周期:
創(chuàng)建信號-訂閱信號-發(fā)送信號-銷毀信號 -
信號響應(yīng)流程分析
