iOS--ReactiveCocoa淺究

導(dǎo)語:

之前一直看我?guī)煾冈谘芯宽憫?yīng)式編程,但是在項(xiàng)目中一直沒有用到,然后我也懶得看,(對,我沒有上進(jìn)心??)。這段時間閑來無事,簡單的研究了一下,百閑之中整理出了文檔。
ReactiveCocoa,很神奇的一個框架,為我們提供了很多方便的方法,去處理事件的操作,如UITextField不需要實(shí)現(xiàn)其代理方法就可以獲取到輸入框中的內(nèi)容。RAC的主要好處是它提供了一個信號RACSignal類,來統(tǒng)一處理Cocoa的各種行為,包括delegate,block,target-action機(jī)制,notification和KVO等等,可以把要處理的事件和監(jiān)聽的代碼放在一起,使用起來也及其簡便易懂。
它在5.0以后詳細(xì)的拆分為了四個庫:ReactiveCocoa、ReactiveSwift、ReactiveObjC、ReactiveObjCBridge。
其中ReactiveCocoa適用于純Swift項(xiàng)目;
ReactiveObjC適用于純OC項(xiàng)目;
本文主要講下ReactiveObjC的應(yīng)用,swift目前項(xiàng)目中還沒有普及,等普及之后再研究啦。

導(dǎo)入

1.手動導(dǎo)入,github上都有地址,自己去下載導(dǎo)入 (https://github.com/ReactiveCocoa/ReactiveCocoa
2.pods導(dǎo)入,這里不是導(dǎo)入 ReactiveCocoa,而是ReactiveObjC,一定不要導(dǎo)入錯了

導(dǎo)入注意事項(xiàng)

1.若項(xiàng)目為Swift,則導(dǎo)入ReactiveCocoa
2.若項(xiàng)目為Swift,則導(dǎo)入ReactiveObjC
3.若項(xiàng)目為Swift和OC混編,則需要將ReactiveObjC和ReactiveCocoa都導(dǎo)入,同時需導(dǎo)入ReactiveObjCBridge,3個庫都是可以用pods導(dǎo)入的
推薦使用pods

platform: 'ios', '8.0'
pod 'ReactiveObjC'

使用

不管是ReactiveCocoa還是ReactiveObjC,其核心就是信號,也就是事件流。

一.創(chuàng)建信號

    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //發(fā)送信號
        [subscriber sendNext:@"我要發(fā)送信號啦"];
        //如果不再發(fā)送信號,最好發(fā)送信號完成,內(nèi)部會自動調(diào)用[RACdisposable disposable]取消訂閱信號
        [subscriber sendCompleted];
        return nil;
    }];
    //訂閱信號, 才會激活信號,信號激活了,才會發(fā)出去,輸出的信號為 “我要發(fā)送信號啦”
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"訂閱信號:%@", x);
    }];

    //或者利用block回調(diào)
    RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //發(fā)送信號
        [subscriber sendNext:@"發(fā)送信號2"];
        //如果不再發(fā)送信號,最好發(fā)送信號完成,內(nèi)部會自動調(diào)用[RACdisposable disposable]取消訂閱信號
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            //block調(diào)用時刻,當(dāng)信號發(fā)送完成后或者發(fā)送錯誤,就會自動執(zhí)行這個block,取消訂閱信號
            //執(zhí)行完block后,當(dāng)前信號就不在被訂閱了
            NSLog(@"信號被銷毀");
        }];
    }];

二.RACSubject使用

    //創(chuàng)建信號
    RACSubject *subject = [RACSubject subject];
    //發(fā)送信號
    [subject sendNext:@"發(fā)送信號"];
    //訂閱信號
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"信號內(nèi)容%@", x);
    }];

三.RACReplaySubject使用

    //1.創(chuàng)建信號
    RACReplaySubject *replaySub = [RACReplaySubject subject];
    //2.創(chuàng)建訂閱者
    [replaySub subscribeNext:^(id  _Nullable x) {
        NSLog(@"哈哈");
    }];
    //3.發(fā)送訂閱者信號
    [replaySub sendNext:@"123456"];
    //RACReplaySubject 發(fā)送數(shù)據(jù)
    //1.保存值
    //2.遍歷所有的訂閱者,發(fā)送數(shù)據(jù)
    //3. 第3條可以放在第2條的前面,即可以先發(fā)送,再訂閱。RACSubject不可以調(diào)整
    //RACReplaySubject與RACSubject區(qū)別:RACReplaySubject可以先發(fā)送信號,在訂閱信號,RACSubject就不可以。
    //使用場景一:如果每個信號被重復(fù)訂閱一次,就需要把之前的值重復(fù)發(fā)送一次,使用重復(fù)提供信號
    //使用場景二:可以設(shè)置capacity數(shù)量來限制緩存的value的數(shù)量,即只緩沖最新的幾個

四.RACTuple元組

RAC的元祖,跟我們OC的數(shù)組其實(shí)是一樣的,它其實(shí)就是封裝了我們OC的數(shù)組

//創(chuàng)建元祖
    RACTuple *tuple1 = [RACTuple tupleWithObjects:@"one", @"two", @"three", @"four", nil];
   
    //從別的數(shù)組中獲取內(nèi)容
    NSArray *array = @[@"five", @"six", @"seven", @"eight"];
    RACTuple *tuple2 = [RACTuple tupleWithObjectsFromArray:array];
    
    //利用RAC宏快速封裝,即把數(shù)據(jù)包裝成元組
    RACTuple *tuple3 = RACTuplePack(@"nine", @"ten", @"eleven", @"twelve");
    
    NSLog(@"取第一個內(nèi)容1:%@", tuple1[0]);
    NSLog(@"取第一個內(nèi)容2:%@", [tuple2 first]);
    NSLog(@"取第一個內(nèi)容3:%@", [tuple3 last]);

五.RACTupleUnpack

把RACTuple元組類解包成對應(yīng)的數(shù)據(jù)
//解包元組,會把元組的值,按照順序賦給參數(shù)里面的變量賦值,即name值為Damon,age值為33

    RACTuple *tuple4 = RACTuplePack(@"Damon", @"33");
    RACTupleUnpack(NSString *name, NSString *age) = tuple4;

六.遍歷ARRAY數(shù)組和Dictionary字典

遍歷ARRAY數(shù)組和Dictionary字典

    NSArray *arrayFruit = @[@"apple", @"banana", @"watermelon", @"lemon"];
    [arrayFruit.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"數(shù)組內(nèi)容:%@", x);
    }];
    
    NSDictionary *dictionary = @{@"紅色":@"red", @"綠色":@"green", @"紫色":@"purple", @"青色":@"cyan",};
    [dictionary.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
        //普通用法
        NSString *key1 = x[0];
        NSString *value1 = x[0];
        NSLog(@"%@--%@", key1, value1);
        
        //高級用法
        //x 是一個元祖,這個宏能夠?qū)ey 和 value拆開
        RACTupleUnpack(NSString *key, NSString *value) = x;
        NSLog(@"字典內(nèi)容%@:%@", key,value);
    }];

或者使用RAC集合,即數(shù)組轉(zhuǎn)RAC集合

RACSequence *sequence = arrayFruit.rac_sequence;
    //把集合轉(zhuǎn)為信號
    RACSignal *racSignal = sequence.signal;
    //訂閱集合信號,內(nèi)部會自動遍歷所有的元素發(fā)出來
    [racSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"11--%@", x);
    }];

七.數(shù)組內(nèi)容全部替換為0

第一個是單個操作,第二個是一次性全部置換,2個方法都不會改變原數(shù)組內(nèi)容,操作完成后都會生成一個新的數(shù)組,省去了創(chuàng)建可變數(shù)組然后遍歷出來單個添加的操作

    /*內(nèi)容單個替換*/
    NSArray *aryChange1 = @[@"11", @"22", @"33", @"44"];
    NSArray *newAryChange1 = [[array.rac_sequence map:^id _Nullable(id  _Nullable value) {
        //將所有內(nèi)容替換為0
        return @"0";
    }] array];
    NSLog(@"單個替換的數(shù)組為:%@", newAryChange1);
    
    /*內(nèi)容一次性替換*/
    NSArray *aryChange2 = @[@"111", @"222", @"333", @"444"];
    NSArray *newAryChange2 = [[aryChange2.rac_sequence mapReplace:@"0"] array];
    NSLog(@"一次性替換的數(shù)組為:%@", newAryChange2);

八.監(jiān)聽UITextField的輸入改變

可以省去設(shè)置delegate 和實(shí)現(xiàn)代理方法的步驟

//監(jiān)聽TextField的輸入(內(nèi)容改變就會調(diào)用)
    UITextField *textField = [[UITextField alloc] init];
    
    textField.frame = CGRectMake(20, 100, 200, 60);
    textField.backgroundColor = [UIColor grayColor];
    [self.view addSubview:textField];
    
    RACSignal *signalText = [textField rac_textSignal];
    [signalText subscribeNext:^(id  _Nullable x) {
        NSLog(@"輸入框內(nèi)容1:%@", x);
    }];
    //或者添加監(jiān)聽條件
    [[textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
        //表示文字長度 大于5時才會調(diào)用下面的block
        return value.length > 5;
    }] subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"輸入框內(nèi)容2%@", x);
    }];

九.監(jiān)聽button事件

可以省去addTarget添加事件和創(chuàng)建方法的步驟

    UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    btn.frame = CGRectMake(10, 200, 50, 50);
    btn.backgroundColor = [UIColor greenColor];
    [self.view addSubview:btn];
    [[btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        //點(diǎn)擊時觸發(fā)的事件
        NSLog(@"按鈕被點(diǎn)擊了 %@", x);
    }];

十.登錄按鈕狀態(tài)實(shí)時監(jiān)聽

下面表示只有用戶名和密碼輸入框內(nèi)容都大于0時,登錄按鈕才可以點(diǎn)擊,而且狀態(tài)是實(shí)時監(jiān)聽的,一句代碼就能完成這個功能

  RAC(loginBtn, enabled) = [RACSignal combineLatest:@[user.rac_textSignal, psd.rac_textSignal] reduce:^id _Nonnull{
          return @(user.text.length && psd.text.length);
  }];

十一.監(jiān)聽notification通知事件

可以省去在-(void)dedalloc{}中清除通知和監(jiān)聽通知創(chuàng)建方法的步驟

  [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
        NSLog(@"鍵盤彈起 %@", x);
   }];

十二.代替delegate代理方法

可以省去監(jiān)聽以及設(shè)置delegate的步驟,下面表示只要view中執(zhí)行了clickMe這個方法,就會發(fā)送信號執(zhí)行這個回調(diào)

    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [[button rac_signalForSelector:@selector(clickMe)] subscribeNext:^(RACTuple * _Nullable x) {
        NSLog(@"按鈕被點(diǎn)擊了");
    }];

十三.代替KVO監(jiān)聽

可以代替KVO監(jiān)聽,下面表示把監(jiān)聽view的frame屬性改變轉(zhuǎn)換成信號,只要值改變就會發(fā)送信號

    UILabel *label = [[UILabel alloc] init];
    label.frame = CGRectMake(120, 350, 100, 40);
    label.text = @"我是label";
    [self.view addSubview:label];
    //   OC 寫法
    // [label addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
    [[label rac_valuesForKeyPath:@"frame" observer:self] subscribeNext:^(id  _Nullable x) {
        NSLog(@"label的屬性改變了:%@", x);//x是監(jiān)聽屬性的改變結(jié)果
    }];
    //或者這樣寫
    //    [RACObserve(label, frame) subscribeNext:^(id  _Nullable x) {
    //        NSLog(@"屬性的改變 %@", x);
    //    }];

十四.代替NSTimer計(jì)時器

  self.disposable = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
        NSLog(@"當(dāng)前時間:%@", x);
        //關(guān)閉計(jì)時器
        [self.disposable dispose];
    }];

以上基本上就是RAC常用的方法,
參考文檔:
1.http://www.itdecent.cn/p/0845b1a07bfa
2.http://www.itdecent.cn/p/68ae979ba814?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容