iOS--ReactiveCocoa浅究

导语:

之前一直看我师父在研究响应式编程,但是在项目中一直没有用到,然后我也懒得看,(对,我没有上进心😆)。这段时间闲来无事,简单的研究了一下,百闲之中整理出了文档。
ReactiveCocoa,很神奇的一个框架,为我们提供了很多方便的方法,去处理事件的操作,如UITextField不需要实现其代理方法就可以获取到输入框中的内容。RAC的主要好处是它提供了一个信号RACSignal类,来统一处理Cocoa的各种行为,包括delegate,block,target-action机制,notification和KVO等等,可以把要处理的事件和监听的代码放在一起,使用起来也及其简便易懂。
它在5.0以后详细的拆分为了四个库:ReactiveCocoa、ReactiveSwift、ReactiveObjC、ReactiveObjCBridge。
其中ReactiveCocoa适用于纯Swift项目;
ReactiveObjC适用于纯OC项目;
本文主要讲下ReactiveObjC的应用,swift目前项目中还没有普及,等普及之后再研究啦。

导入

1.手动导入,github上都有地址,自己去下载导入 (https://github.com/ReactiveCocoa/ReactiveCocoa
2.pods导入,这里不是导入 ReactiveCocoa,而是ReactiveObjC,一定不要导入错了

导入注意事项

1.若项目为Swift,则导入ReactiveCocoa
2.若项目为Swift,则导入ReactiveObjC
3.若项目为Swift和OC混编,则需要将ReactiveObjC和ReactiveCocoa都导入,同时需导入ReactiveObjCBridge,3个库都是可以用pods导入的
推荐使用pods

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

使用

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

一.创建信号

    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //发送信号
        [subscriber sendNext:@"我要发送信号啦"];
        //如果不再发送信号,最好发送信号完成,内部会自动调用[RACdisposable disposable]取消订阅信号
        [subscriber sendCompleted];
        return nil;
    }];
    //订阅信号, 才会激活信号,信号激活了,才会发出去,输出的信号为 “我要发送信号啦”
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅信号:%@", x);
    }];

    //或者利用block回调
    RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //发送信号
        [subscriber sendNext:@"发送信号2"];
        //如果不再发送信号,最好发送信号完成,内部会自动调用[RACdisposable disposable]取消订阅信号
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            //block调用时刻,当信号发送完成后或者发送错误,就会自动执行这个block,取消订阅信号
            //执行完block后,当前信号就不在被订阅了
            NSLog(@"信号被销毁");
        }];
    }];

二.RACSubject使用

    //创建信号
    RACSubject *subject = [RACSubject subject];
    //发送信号
    [subject sendNext:@"发送信号"];
    //订阅信号
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"信号内容%@", x);
    }];

三.RACReplaySubject使用

    //1.创建信号
    RACReplaySubject *replaySub = [RACReplaySubject subject];
    //2.创建订阅者
    [replaySub subscribeNext:^(id  _Nullable x) {
        NSLog(@"哈哈");
    }];
    //3.发送订阅者信号
    [replaySub sendNext:@"123456"];
    //RACReplaySubject 发送数据
    //1.保存值
    //2.遍历所有的订阅者,发送数据
    //3. 第3条可以放在第2条的前面,即可以先发送,再订阅。RACSubject不可以调整
    //RACReplaySubject与RACSubject区别:RACReplaySubject可以先发送信号,在订阅信号,RACSubject就不可以。
    //使用场景一:如果每个信号被重复订阅一次,就需要把之前的值重复发送一次,使用重复提供信号
    //使用场景二:可以设置capacity数量来限制缓存的value的数量,即只缓冲最新的几个

四.RACTuple元组

RAC的元祖,跟我们OC的数组其实是一样的,它其实就是封装了我们OC的数组

//创建元祖
    RACTuple *tuple1 = [RACTuple tupleWithObjects:@"one", @"two", @"three", @"four", nil];
   
    //从别的数组中获取内容
    NSArray *array = @[@"five", @"six", @"seven", @"eight"];
    RACTuple *tuple2 = [RACTuple tupleWithObjectsFromArray:array];
    
    //利用RAC宏快速封装,即把数据包装成元组
    RACTuple *tuple3 = RACTuplePack(@"nine", @"ten", @"eleven", @"twelve");
    
    NSLog(@"取第一个内容1:%@", tuple1[0]);
    NSLog(@"取第一个内容2:%@", [tuple2 first]);
    NSLog(@"取第一个内容3:%@", [tuple3 last]);

五.RACTupleUnpack

把RACTuple元组类解包成对应的数据
//解包元组,会把元组的值,按照顺序赋给参数里面的变量赋值,即name值为Damon,age值为33

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

六.遍历ARRAY数组和Dictionary字典

遍历ARRAY数组和Dictionary字典

    NSArray *arrayFruit = @[@"apple", @"banana", @"watermelon", @"lemon"];
    [arrayFruit.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"数组内容:%@", 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 是一个元祖,这个宏能够将key 和 value拆开
        RACTupleUnpack(NSString *key, NSString *value) = x;
        NSLog(@"字典内容%@:%@", key,value);
    }];

或者使用RAC集合,即数组转RAC集合

RACSequence *sequence = arrayFruit.rac_sequence;
    //把集合转为信号
    RACSignal *racSignal = sequence.signal;
    //订阅集合信号,内部会自动遍历所有的元素发出来
    [racSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"11--%@", x);
    }];

七.数组内容全部替换为0

第一个是单个操作,第二个是一次性全部置换,2个方法都不会改变原数组内容,操作完成后都会生成一个新的数组,省去了创建可变数组然后遍历出来单个添加的操作

    /*内容单个替换*/
    NSArray *aryChange1 = @[@"11", @"22", @"33", @"44"];
    NSArray *newAryChange1 = [[array.rac_sequence map:^id _Nullable(id  _Nullable value) {
        //将所有内容替换为0
        return @"0";
    }] array];
    NSLog(@"单个替换的数组为:%@", newAryChange1);
    
    /*内容一次性替换*/
    NSArray *aryChange2 = @[@"111", @"222", @"333", @"444"];
    NSArray *newAryChange2 = [[aryChange2.rac_sequence mapReplace:@"0"] array];
    NSLog(@"一次性替换的数组为:%@", newAryChange2);

八.监听UITextField的输入改变

可以省去设置delegate 和实现代理方法的步骤

//监听TextField的输入(内容改变就会调用)
    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(@"输入框内容1:%@", x);
    }];
    //或者添加监听条件
    [[textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
        //表示文字长度 大于5时才会调用下面的block
        return value.length > 5;
    }] subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"输入框内容2%@", x);
    }];

九.监听button事件

可以省去addTarget添加事件和创建方法的步骤

    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) {
        //点击时触发的事件
        NSLog(@"按钮被点击了 %@", x);
    }];

十.登录按钮状态实时监听

下面表示只有用户名和密码输入框内容都大于0时,登录按钮才可以点击,而且状态是实时监听的,一句代码就能完成这个功能

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

十一.监听notification通知事件

可以省去在-(void)dedalloc{}中清除通知和监听通知创建方法的步骤

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

十二.代替delegate代理方法

可以省去监听以及设置delegate的步骤,下面表示只要view中执行了clickMe这个方法,就会发送信号执行这个回调

    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
    [[button rac_signalForSelector:@selector(clickMe)] subscribeNext:^(RACTuple * _Nullable x) {
        NSLog(@"按钮被点击了");
    }];

十三.代替KVO监听

可以代替KVO监听,下面表示把监听view的frame属性改变转换成信号,只要值改变就会发送信号

    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是监听属性的改变结果
    }];
    //或者这样写
    //    [RACObserve(label, frame) subscribeNext:^(id  _Nullable x) {
    //        NSLog(@"属性的改变 %@", x);
    //    }];

十四.代替NSTimer计时器

  self.disposable = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
        NSLog(@"当前时间:%@", x);
        //关闭计时器
        [self.disposable dispose];
    }];

以上基本上就是RAC常用的方法,
参考文档:
1.https://www.jianshu.com/p/0845b1a07bfa
2.https://www.jianshu.com/p/68ae979ba814?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343