ReactiveCocoa

1.ReactiveCocoa做用

    在咱們iOS開發過程當中,當某些事件響應的時候,須要處理某些業務邏輯,這些事件都用不一樣的方式來處理。好比按鈕的點擊使用action,ScrollView滾動使用delegate,屬性值改變使用KVO等系統提供的方式。其實這些事件,均可以經過RAC處理。數組

    podfile寫入 cocopods安裝緩存

use_frameworks!
pod 'ReactiveCocoa', '~> 4.0.2-alpha-1'

2.RACSiganl

    RACSiganl:信號類,只是表示當數據改變時,信號內部會發出數據,它自己不具有發送信號的能力,而是交給內部一個訂閱者去發出。網絡

   默認一個信號爲冷信號,也就是隻有訂閱了,這個信號纔會變成熱信號,值改變纔會觸發ide

    RACSubscriber:表示訂閱者的意思,用於發送信號,這是一個協議,不是一個類,只要遵照這個協議,而且實現方法才能成爲訂閱者。經過create建立的信號,都有一個訂閱者,幫助他發送數據。性能

    RACDisposable:用於取消訂閱或者清理資源,當信號發送完成或者發送錯誤的時候,就會自動觸發它。只有當信號被銷燬的時候就會自定取消,也就說信號爲全局變量的時候,可以持續不被銷燬。spa

   使用步驟:1.建立信號->2.訂閱信號->3.發送信號設計

    //1.建立信號(冷信號)
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        //block調用時刻:每當有訂閱者訂閱信號,就會調用block
        
        //2.發送信號
        subscriber調用:只有信號被訂閱就會調用
        [subscriber sendNext:@1];
        
        //若是再也不發送數據,最好發送信號完成,內部會自動調用[RACDisposable disposable]取消訂閱
        [subscriber sendCompleted];
        
        return [RACDisposable disposableWithBlock:^{
            //block調用時刻:當信號發送完成或者發送錯誤,就會自動執行這個block,取消訂閱
            NSLog(@"信號被銷燬");
        }];
    }];
    
    
    //3.訂閱信號(熱信號)
    [signal subscribeNext:^(id x) {
        //block調用時刻:每當有信號發送數據,就會調用該方法
        //發送信號的時候,將信息先保存在nextBlock,而後訂閱者再從其取出
        NSLog(@"接收到的數據:%@",x);
    }];

 

3.RACSubject與RACReplaySubject

     RACSubject:信號提供者,本身能夠充當信號,又能發送信號。subject能夠想成是signal的變體,就像NSMutableArray相對於NSArray⼀同樣。它們是非RAC的代碼和RAC代碼之間的橋樑。代理

    RACReplaySubject:重複提供信號類,RACSubject的子類。指針

    RACReplaySubject與RACSubject區別:code

         1).RACReplaySubject能夠先發送信號,再訂閱信號,RACSubject就不能夠。

         2).RACReplaySubject能夠設置capacity數量來限制緩存的value的數量,即只緩充最新的幾個值。

         3).若是一個信號每被訂閱一次,就須要把以前的值重複發送一遍,就須要使用RACReplaySubject

    //1.建立信號
    RACSubject *subject = [RACSubject subject];
    
    //2.訂閱信號
    [subject subscribeNext:^(id x) {
        //block調用時刻:當信號發出新值,就會調用
        NSLog(@"第一個訂閱者%@",x);
    }];
    [subject subscribeNext:^(id x) {
        //block調用時刻:當信號發出新值,就會調用
        NSLog(@"第二個訂閱者%@",x);
    }];
    
    //3.發送信號
    [subject sendNext:@"1"];
    
  
    
      //1.建立信號
    RACReplaySubject *replaySubject = [RACReplaySubject subject];
   // RACReplaySubject *replaySubject = [RACReplaySubject replaySubjectWithCapacity:0];
    
    //2.發送信號
    [replaySubject sendNext:@1];
    [replaySubject sendNext:@2];
    
    //3.訂閱信號
    [replaySubject subscribeNext:^(id x) {
        NSLog(@"第一個訂閱者%@",x);
    }];
    [replaySubject subscribeNext:^(id x) {
        NSLog(@"第二個訂閱者%@",x);
    }];

    RACSubject替代代理

情景:跳轉到另外一個vc,TwoVC發送通知,VC收到回調的通知

VC
- (IBAction)click:(UIButton *)sender {

    TwoViewController *twoVC = [[TwoViewController alloc] init];
    
    //設置代理信號
    twoVC.delegateSubject = [RACSubject subject];
    
    //訂閱代理信號
    [twoVC.delegateSubject subscribeNext:^(id x) {
        NSLog(@"點擊了通知按鈕,%@",x);
    }];
    
    //跳轉
    [self presentViewController:twoVC animated:YES completion:nil];

}

TwoVC
    if (self.delegateSubject) {
        //發送信號
        [self.delegateSubject sendNext:@"已跳轉到TwoVC"];
    }

 

4.RACScheduler

    延時執行發送信號

RACSubject *subject = [RACSubject subject];
    RACSubject *replaySubject = [RACReplaySubject subject];

    [[RACScheduler mainThreadScheduler] afterDelay:0.1 schedule:^{
        [subject subscribeNext:^(id x) {
            NSLog(@"Subscriber 1 get a next value: %@ from subject", x);
        }];
        [replaySubject subscribeNext:^(id x) {
            NSLog(@"Subscriber 1 get a next value: %@ from replay subject", x);
        }];
    }];

5.RACSequence

    RACTuple:元組類,相似NSArray,用來包裝值.

    RACSequence:RAC中的集合類,用於代替NSArray,NSDictionary,可使用它來快速遍歷數組和字典。

    //第一步: 把數組轉換成集合RACSequence             numbers.rac_sequence
    // 第二步: 把集合RACSequence轉換RACSignal信號類    numbers.rac_sequence.signal
    // 第三步: 訂閱信號,激活信號,會自動把集合中的全部值,遍歷出來。    
    // 1.遍歷數組
    NSArray *numbers = @[@1,@2,@3,@4];
    [numbers.rac_sequence.signal subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    // 2.遍歷字典,遍歷出來的鍵值對會包裝成RACTuple(元組對象)
    NSDictionary *dict = @{@"name":@"xiaoming",@"age":@18};    
    // 解包元組,會把元組的值,按順序給參數裏面的變量賦值
    // 至關於如下寫法
    //        NSString *key = x[0];
    //        NSString *value = x[1];
    [dict.rac_sequence.signal subscribeNext:^(id x) {
        RACTupleUnpack(NSString *name,NSString *age) = x;
        NSLog(@"%@ %@",name,age);
    }];

字典轉模型

    //1.OC寫法
    NSDictionary *dict1 = @{@"name":@"xiaoming",@"age":@18};
    NSDictionary *dict2 = @{@"name":@"xiaohua",@"age":@20};
    NSArray *arrs = @[dict1,dict2];
    
    NSMutableArray *items = [NSMutableArray array];
    
    for (NSDictionary *dict in arrs) {
        FlagItem *item = [FlagItem flagWithDict:dict];
        [items addObject:item];
    }
    
    //2.RAC寫法
    [arrs.rac_sequence.signal subscribeNext:^(id x) {
        //遍歷RAC字典
        FlagItem *item = [FlagItem flagWithDict:x];
        [items addObject:item];
    }];
    
    //3.高級RAC寫法
    // map:映射的意思,目的:把原始值value映射成一個新值
    // array: 把集合轉換成數組
    // 底層實現:當信號被訂閱,會遍歷集合中的原始值,映射成新值,而且保存到新的數組裏。
    NSArray *flags = [[arrs.rac_sequence map:^id(id value) {
    
        return [FlagItem flagWithDict:value];
    
    }] array];
    NSLog(@"%@",flags);

 

6.RACCommand

    建立並訂閱響應action的信號。 一般command是由UI觸發的,像一個按鈕被點擊時。當command被觸發時,控件會⾃自動被禁⽤。

   有數據改變使用RACSignal 有事件處理須要RACCommand

    RACCommand設計思想:內部signalBlock爲何要返回一個信號,這個信號有什麼用。

        1.在RAC開發中,一般會把網絡請求封裝到RACCommand,直接執行某個RACCommand就能發送請求。

         2.當RACCommand內部請求到數據的時候,須要把請求的數據傳遞給外界,這時候就須要經過signalBlock返回的信號傳遞了。

    使用場景,監聽按鈕點擊,網絡請求

    //1.建立命令
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        
        NSLog(@"執行命令");
        
        // signalBlock必需要返回一個信號,不能傳nil,若是不想要傳遞信號,直接建立空的信號。
        //return [RACSignal empty];
        
        //2.建立信號,用來傳遞數據
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            
            [subscriber sendNext:@"請求數據"];
            
            //RACCommand中信號若是數據傳遞完,必須調用[subscriber sendCompleted],這時命令纔會執行完畢,不然永遠處於執行中。
            [subscriber sendCompleted];
        
            return nil;
        }];
    }];
    
    //RACCommand須要被強引用,不然接收不到RACCommand中的信號,所以RACCommand中的信號是延遲發送的。
    _command = command;
    
    //3.訂閱信號
    [command.executionSignals subscribeNext:^(id x) {
        
        [x subscribeNext:^(id x) {
            NSLog(@"%@",x);
        }];
    }];
    
    //RAC高級用法:
    // switchToLatest:用於signal of signals,獲取signal of signals發出的最新信號,也就是能夠直接拿到RACCommand中的信號,不須要訂閱信號
    [command.executionSignals.switchToLatest subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    
    //監聽命令是否執行完畢,默認會來一次,能夠直接跳過,skip表示跳過第一次命令
    [[command.executing skip:1] subscribeNext:^(id x) {
        
        if ([x boolValue] == YES) {
            NSLog(@"正在執行");
        }else{
            NSLog(@"執行完成");
        }
    }];
    
    
    //4.執行命令
    [self.command execute:nil];

 

7.RACMulticastConnection

    用於當一個信號,被屢次訂閱時,爲了保證建立信號時,避免屢次調用建立信號中的block,形成反作用,可使用這個類處理。例如:當有2個RACSignal訂閱信心的時候,就須要發送兩次RACSiagnal的信號,執行兩次block操做。而使用RACMulticastConnection鏈接,對signal pulish處理就不會屢次建立。

  RACMulticastConnection會使信號變成熱信號:

  1. 熱信號是主動的,即便你沒有訂閱事件,它仍然會時刻推送如例子,信號在50秒被建立,51秒的時候1這個值就推送出來了,可是當時尚未訂閱者。而冷信號是被動的,只有當你訂閱的時候,它纔會發送消息
  2. 熱信號能夠有多個訂閱者,是一對多,信號能夠與訂閱者共享信息如例子,訂閱者1和訂閱者2是共享的,他們都能在同一時間接收到3這個值。而冷信號只能一對一,當有不一樣的訂閱者,消息會重新完整發送咱們能夠觀察到兩個訂閱者沒有聯繫,都是基於各自的訂閱時間開始接收消息的。
    //1.建立信號a
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSLog(@"發送請求");
        
        [subscriber sendNext:@1];
        
        return nil;
    }];
    
    //2,建立鏈接
    RACMulticastConnection *connect = [signal publish];
    
    //3.訂閱信號。即便訂閱了,還沒激活信號
    [[RACScheduler mainThreadScheduler] afterDelay:1.1 schedule:^{
        [signal subscribeNext:^(id x) {
            NSLog(@"Subscriber 1 recveive: %@", x);
        }];
    }];

    [[RACScheduler mainThreadScheduler] afterDelay:2.1 schedule:^{
        [signal subscribeNext:^(id x) {
            NSLog(@"Subscriber 2 recveive: %@", x);
        }];
    }];
    
    //4.鏈接,激活信號
    [connect connect];

2015-08-12 11:07:49.943 RACDemos[9418:1186344] Signal was created.
2015-08-12 11:07:52.088 RACDemos[9418:1186344] Subscriber 1 recveive: 2
2015-08-12 11:07:53.044 RACDemos[9418:1186344] Subscriber 1 recveive: 3
2015-08-12 11:07:53.044 RACDemos[9418:1186344] Subscriber 2 recveive: 3

8.ReactiveCocoa的其餘用法

    8.1 代替代理:rac_signalForSelector:用於替代代理。

    8.2 代替KVO :rac_valuesAndChangesForKeyPath:用於監聽某個對象的屬性改變。

    8.3 監聽事件:rac_signalForControlEvents:用於監聽某個事件。

    8.4 代替通知:rac_addObserverForName:用於監聽某個通知。

    8.5 處理當界面有屢次請求時,須要都獲取到數據時,才能展現界面

    8.6 map轉換信號類型

    8.7 聚合信號

    8.8 同時訂閱2個信號

    8.9 信號依賴,信號A完成後才能夠訂閱B

    8.10 發送信號前先發送一個消息

    8.11 超時不發送消息

    8.12 接收前幾條消息或者後幾條信息

    8.13 條件返回YES/NO才能發送消息

    8.14 跳過前幾回消息

    8.15 信號數組都發送過消息觸發方法

    8.16 監聽文本框文字改變:rac_textSignal:只要文本框發出改變就會發出這個信號、判斷字符長度、間隔時間、忽略字符。

使用注意:幾個信號,參數一的方法就幾個參數,每一個參數對應信號發出的數據。

 1.代替代理
// 需求:自定義redView,監聽紅色view中按鈕點擊
// 以前都是須要經過代理監聽,給紅色View添加一個代理屬性,點擊按鈕的時候,通知代理作事情
// rac_signalForSelector:把調用某個對象的方法的信息轉換成信號,就要調用這個方法,就會發送信號。
// 這裏表示只要redV調用btnClick:,就會發出信號,訂閱就行了。
[[redV rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {
    NSLog(@"點擊紅色按鈕");
}];

 2.KVO
// 把監聽redV的center屬性改變轉換成信號,只要值改變就會發送信號
// observer:能夠傳入nil
[[redV rac_valuesAndChangesForKeyPath:@"center" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
    
    NSLog(@"%@",x);
    
}];

 3.監聽事件
// 把按鈕點擊事件轉換爲信號,點擊按鈕,就會發送信號
[[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
    
    NSLog(@"按鈕被點擊了");
}];
eg:
//建立一個信號
-(RACSignal *)signInSignal{
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [self signInWithUsername:_usernameTxt.text
                              password:_pwdnameTxt.text complete:^(BOOL sucess) {
                                  NSLog(@"success : %d",sucess);
                                  [subscriber sendNext:@(YES)];
                                  [subscriber sendCompleted];
                              }];
//這個block的返回值是一個RACDisposable對象,它容許你在一個訂閱被取消時執行一些清理工做。當前的信號不須要執行清理操做,因此返回nil就能夠了。
        return  nil;
    }];
}
    //map 將按鈕點擊事件轉換爲登陸信號,subscribe輸出的是內部信號。但咱們用flattenMap,即能訂閱外部信號裏的內部信號
    [[[_sureBtn rac_signalForControlEvents:UIControlEventTouchUpInside] flattenMap:^id(id value) {
        return [self signInSignal];//轉換成該信號,然後訂閱它,在subscribeNext輸出該內部信號
    }] subscribeNext:^(NSNumber *x) {
        NSLog(@"sign in result :%@",x);
        BOOL sucess = [x boolValue];
        _sureBtn.enabled = sucess;
        if (sucess) {
            [self presentViewController:[[ViewController1 alloc] init] animated:YES completion:nil];
        }
        
    }];


 4.代替通知
// 把監聽到的通知轉換信號
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
    NSLog(@"鍵盤彈出");
}];

 5.處理多個請求,都返回結果的時候,統一作處理.
RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    
    // 發送請求1
    [subscriber sendNext:@"發送請求1"];
    return nil;
}];
RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    // 發送請求2
    [subscriber sendNext:@"發送請求2"];
    return nil;
}];
// 使用注意:幾個信號,參數一的方法就幾個參數,每一個參數對應信號發出的數據。
[self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[request1,request2]];
}
// 更新UI
- (void)updateUIWithR1:(id)data r2:(id)data1
{
    NSLog(@"更新UI%@  %@",data,data1);
}

6.map轉換信號類型
    //map的做用是類型轉換,返回轉換後的處理的值,這裏NSString->Bool->UIColor。subscribeNext是訂閱,返回的銷燬信號對象。
    RAC(_pwdnameTxt,backgroundColor) = [_pwdnameTxt.rac_textSignal map:^id(NSString *pwd) {
        NSLog(@"pwd:%@",pwd);
        return pwd.length>0 ? [UIColor clearColor]:[UIColor yellowColor];
    }];
    
    RAC(_usernameTxt,backgroundColor) = [_usernameTxt.rac_textSignal map:^id(NSString *usename) {
        NSLog(@"usename:%@",usename);
        return usename.length > 0 ? [UIColor clearColor]:[UIColor yellowColor];
    }];

7.聚合信號
 //聯合多個信號,進行操做
    //聚合信號
    RACSignal *signUpSignal = [RACSignal combineLatest:@[_pwdnameTxt.rac_textSignal,_usernameTxt.rac_textSignal] reduce:^id(NSString *username,NSString *pwd){
        NSLog(@"%@ %@",username,pwd);
        return @(username.length > 0 && pwd.length >0);
    }];
    RAC(_sureBtn,enabled) = signUpSignal;

8.同時訂閱2個信號
    [signalA merge:signalB subscribeNext:^(id x) {
        NSLog(@"x %@",x);
    }];

9.信號依賴,信號A完成後纔會訂閱B信號
    [signalA concat:signalB subscribeNext:^(id x) {
        NSLog(@"x %@",x);
    }];

10.發送信號前先發送一個消息
    _signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:sender.text];
        [subscriber sendCompleted];
        return nil;
    }] startWith:@"RAC"];

    [_signal subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];

11.超時不發送消息
  //超時2秒,不發送消息就不會發送了。但仍是能收到startwith的消息
    _signal = [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{
            [subscriber sendNext:sender.text];
        }];
        
        [subscriber sendCompleted];
        return nil;
    }] startWith:@"RAC"] timeout:2 onScheduler:[RACScheduler mainThreadScheduler]];

12.接收前幾條消息 take;接收後幾條消息 takelast
    _signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [subscriber sendNext:sender.text];
        [subscriber sendNext:@1];
        [subscriber sendNext:@2];
        [subscriber sendNext:@3];
        
        [subscriber sendCompleted];
        return nil;
    }] take:2];

13.當takeUntilBlock返回NO的時候發送消息,當takeWhileBlock返回YES的時候發送消息
    _signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [subscriber sendNext:@1];
        [subscriber sendCompleted];
        return nil;
        
    }] takeUntilBlock:^BOOL(id x) {
        return NO;
    }];

14.跳過前幾回消息 skip
    _signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        [subscriber sendNext:@1];
        [subscriber sendNext:@2];
        [subscriber sendNext:@3];
        [subscriber sendNext:@4];
        [subscriber sendCompleted];
        return nil;
        
    }] skip:2];

15.rac_liftSelector:withSignalsFromArray:Signals:
  當傳入的Signals(信號數組),每個signal都至少sendNext過一次,就會去觸發第一個selector參數的方法。

16.文本框
   //a.監聽文本框的文字改變
[_textField.rac_textSignal subscribeNext:^(id x) {
    
    NSLog(@"文字改變了%@",x);
}];
  //b.若是須要判斷文字長度能夠加入filter
    [[_textField.rac_textSignal filter:^BOOL(id value) {
        NSString *text  = value;
        return text.length>3;
        
    }]subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
  //c.沒輸入一個字符就發送消息,會帶來性能消耗。能夠設置一個間隔時間,大於它的時候發送信號
    [[_txtField.rac_textSignal throttle:0.3] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
  //d.忽略某些字符,多用於忽略空格
    [[_txtField.rac_textSignal ignore:@""] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];

 

9.ReactiveCocoa宏用法

    9.1 RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用於給某個對象的某個屬性綁定。

//把左邊對象的某個屬性綁定一個信號,只要發出信號,就會把右邊對象的信號的內容給左邊對象的屬性賦值
- (void)define_dattributesBind
{
    // 只要文本框文字改變,就會修改label的文字
    [_myTxtField.rac_textSignal subscribeNext:^(id x) {
        _myLabel.text = x;
    }];
    
    RAC(_myLabel,text) = _myTxtField.rac_textSignal;
}

   RAC快速實現秒錶    

RAC(self.label,text) = [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] map:^id(NSDate *value) {
        
        return value.description;
    }];

9.2 RACObserve(self, name):監聽某個對象的某個屬性,返回的是信號。

    [RACObserve(self.view, center) subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
    }];

   9.3 避免block循環引用,@weakify(Obj)和@strongify(Obj),通常兩個都是配套使用,須要手動導入RACEXTScope.h纔可使用。

    //self對signal進行了強引用,而signal又對self強引用。須要避免block內循環引用。
    //把self轉換成一個弱指針
    @weakify(self);
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        @strongify(self);
        NSLog(@"%@",self.view);
        return nil;
    }];
    
    _signal = signal;

    9.4 Tuple

    //RACTuplePack:把數據包裝成RACTuple(元組類)
    RACTuple *tuple = RACTuplePack(@"xmg",@20);
    NSLog(@"%@",tuple);
    
    //RACTupleUnpack:把RACTuple(元組類)解包成對應的數據。
    // 解包元組,會把元組的值,按順序給參數裏面的變量賦值
    RACTupleUnpack(NSString *name,NSNumber *age) = tuple;
    NSLog(@"%@ %@",name,age);
相關文章
相關標籤/搜索