ReactiveCocoa RAC 响应式函数编程(FRP)

RACSignal

/** * 手动创建信号 * * 其中的:RACDisposable 当调用 [subscriber sendCompleted]或者[subscriber sendError:nil] 自动调用 可以返回nil * */
    RACSignal *signal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {


        [subscriber sendNext:@"signal send next"];
        [subscriber sendCompleted];

        //[subscriber sendError:nil];


        return [RACDisposable disposableWithBlock:^{
            NSLog(@"signal 发送完成销毁");
        }];
    }];


    /** * 给信号添加订阅者 * *  @param x 调用订阅者的 [subscriber sendNext:@"signal send next"] subscribeNext 中传的就是sendNext传递的value * * *  @return RACDisposable 可用于手动调用销毁信号 */
    RACDisposable *disposal= [signal subscribeNext:^(id x) {
        NSLog(@"signal 给订阅者发送信号:%@",x);
    } error:^(NSError *error) {
         NSLog(@"signal 发生错误");
    }];

    //[disposal dispose];
    [[self rac_signalForSelector:@selector(test)] subscribeNext:^(id x) {
        NSLog(@"test 调用");//执行了test 方法后调用next
    }];

事件,KVO,通知等

// @weakify(self) 避免循环引用
    @weakify(self);
    /** * 监听文本的输入 * * @param x TextFiled 的text 类型为id 可以直接修改NSString * * @return */
    [[self.mTextFiled1 rac_textSignal] subscribeNext:^(NSString *value) {
        NSLog(@"文本1:%@",value);
    }];



    /** * 监听按钮点击 * * @param x value 为按钮本身 * * @return */
    [[self.mButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *button) {
        button.backgroundColor=[UIColor yellowColor];
        NSLog(@"点击了按钮");
    }];

    /** * kvo * * @param self.person target * @param name keypath * skip:1 忽略第一次name的信号,name 第一次为nil 也会发出信号 * @return */
    [[RACObserve(self.person,name)  skip:1]subscribeNext:^(NSString *value) {
        @strongify(self);
        [self.mButton setTitle:value forState:UIControlStateNormal];
    }];

    /** * RAC(target,property)=信号 * RAC 宏 target.property=信号的subscribeNext 传递的值 */
    RAC(self.person,name)=self.mTextFiled1.rac_textSignal;
    /** * 通知 * * @param x 通知 * * @return * takeUntil 取消订阅的信号 self.rac_willDeallocSignal 当前self即将销毁的时候会取消订阅 */
    [[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil] takeUntil:self.rac_willDeallocSignal] subscribeNext:^(id x) {
        NSLog(@"通知%@",x);
    }];

    /* * 代理:创建 =[[RACDelegateProxy alloc] initWithProtocol:@protocol(协议名称)] * 成为代理:vc.delegate=(id<ToViewControllerDelegate>)self.proxy; 强制类型转换即可 */
    self.proxy=[[RACDelegateProxy alloc] initWithProtocol:@protocol(ToViewControllerDelegate)];
    [[self.proxy signalForSelector:@selector(toViewControllerAction:p2:)] subscribeNext:^(RACTuple *value) {

        /** * 元组解包 * p1=value.first p2.value.second */
        RACTupleUnpack(NSString *p1,NSString *p2)=value;

        NSLog(@"ToViewControllerDelegate 的 toViewControllerAction 方法执行了 参数1%@ 参数2:%@",p1,value.second);
    }];

RACSuject

/** * RACSubject 本身就是信号,同时自己发送信号 调用sendNext 其他订阅者可以接收到信号 * RACSubject 创建本身不需要block * RACSubject 常用于代理 */
    RACSubject *racSuject=[RACSubject subject];



    [racSuject subscribeNext:^(id x) {
        NSLog(@"订阅1");
    }];
    [racSuject subscribeNext:^(id x) {
        NSLog(@"订阅2");
    }];

    [racSuject sendNext:racSuject];


    ToViewController *vc=[ToViewController new];
    //创建代理
    vc.signalDelegate=[RACSubject subject];
    //订阅
    [vc.signalDelegate subscribeNext:^(RACTuple *value) {
        /** * 第一个参数设置为int类型 假设多个代理方法的时候,就可以用这种方法去处理代理的不同方法 可以将这个参数设置为常量 */
        switch ([value.first integerValue]) {
            case 1:
                NSLog(@"执行代理方法 参数1:%@ 参数2:%@",value.second,value.third);
                break;
            default:
                break;
        }
    }];
    [self.navigationController pushViewController:vc animated:YES];

RACReplaySubject

/** * RACReplaySubject 可以先发送信号,在订阅 RACSubject 不可以 * RACReplaySubject 先将value 保存起来 有新的订阅者就会将之前的所有的value重新发送一遍 * RACSubject 保存的订阅者,有新的者,遍历订阅者一个个发送 */
    RACReplaySubject *replaySubject=[RACReplaySubject subject];
    [replaySubject sendNext:@"1"];
    [replaySubject sendNext:@"2"];
    [replaySubject subscribeNext:^(id x) {
        NSLog(@"订阅者1 接收的value:%@",x);
    }];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2 * NSEC_PER_SEC)),dispatch_get_main_queue(),^{
        [replaySubject subscribeNext:^(id x) {
            NSLog(@"订阅者2 接收的value:%@",x);
        }];
    });

RACSequence

/** * * RACSequence :集合 * 1.集合转换为集合信号RACSequence rac_sequence * 2.订阅信号中的信号 rac_sequence.signal */
    NSArray *array=@[@1,@2,@3,@4];
    [array.rac_sequence.signal subscribeNext:^(id x) {
        NSLog(@"遍历数组%@",x);
    }];
    NSDictionary *dict=@{@"key1":@1,@"key2":@2};
    [dict.rac_sequence.signal subscribeNext:^(RACTuple *value) {
        NSLog(@"遍历字典 key:%@ value:%@",value.first,value.second);
    }];

RACCommand

/** * RACCommand 命令 多用于网络请求,因为它可以监听sinal的请求状态 * */
    RACCommand *racCommand=[[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        NSLog(@"start command");
        //return [RACSignal empty]; 必须返回信号,不可以返回nil
        return  [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW,dispatch_get_global_queue(0,0),^{
                [subscriber sendNext:@"下载的数据"];
                [subscriber sendCompleted];
            });
            return nil;
        }];
    }];
    /** * 强引用,不然会被销毁 */
    _racCommand=racCommand;

    /** * executionSignals 获取命令中执行返回的信号 */
    [_racCommand.executionSignals subscribeNext:^(id x) {
       [x subscribeNext:^(id x) {
           NSLog(@"订阅1 请求数据 %@",x);
       }];
    }];
    /* _racCommand.executionSignals.switchToLatest 获取最后一个信号 常用获取命令中的信号 */
    [_racCommand.executionSignals.switchToLatest subscribeNext:^(id x) {
            NSLog(@"订阅2 请求的数据%@",x);
    }];
    [[_racCommand.executing skip:1] subscribeNext:^(id x) {
        if([x boolValue]){
            NSLog(@"命令正在执行过程中");
        }else{
            NSLog(@"命令完成");
        }
    }];
    //执行命令
    [self.racCommand execute:@1];

信号绑定

@weakify(self);
    [[self.mTextFiled1.rac_textSignal bind:^RACStreamBindBlock{

        /* * 当原信号发出的时候,就会执行以下block * block 处理原来的信号 * 返回新的信号 RACReturnSignal 需要导入<ReactiveCocoa/RACReturnSignal.h> * (id value,BOOL *stop) value 为原信号的value stop=yes 就会结束绑定 * */
        return ^RACStream *(id value,BOOL *stop){
            *stop=self.stop;
            NSLog(@"处理绑定的信号");
            return [RACReturnSignal return:[NSString stringWithFormat:@"bind%@",value]];
        };
    }] subscribeNext:^(id x) {
        @strongify(self);
        [self.mButton setTitle:x forState:UIControlStateNormal];
    }];


    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,^{
        NSLog(@"取消了绑定");
        self.stop=YES;//2 秒后取消绑定,但是还是会执行一次,因为执行一次后,才会重新赋值为Yes,取消绑定
    });

映射

/** 原理绑定 如果信号发出的值不是信号,映射一般使用Map 发出的值是信号 flattenMap */
    @weakify(self);
    [[self.mTextFiled1.rac_textSignal flattenMap:^RACStream *(id value) {
        NSLog(@"处理绑定的信号");
        return [RACReturnSignal return:[NSString stringWithFormat:@"bind%@",value]];
    }] subscribeNext:^(id x) {
        @strongify(self);
        [self.mButton setTitle:x forState:UIControlStateNormal];
    }];
    /* * 同样还是绑定 常用于修改原信号的返回值,返回自己想要的值 */
    [[self.mTestField2.rac_textSignal map:^id(id value) {
        return [NSString stringWithFormat:@"bind2%@",value];
    }] subscribeNext:^(id x) {
        @strongify(self);
        [self.mButton setTitle:x forState:UIControlStateNormal];
    }];

过滤等

@weakify(self);
    /* * filter返回Yes 的信号才会往下传递 */
    [[self.mTextFiled1.rac_textSignal filter:^BOOL(NSString *value) {
        return [value isEqualToString:@"filter"];
    }] subscribeNext:^(id x) {
        @strongify(self);
        [self.mButton setTitle:x forState:UIControlStateNormal];
    }];
    /* *忽略ignore的value 例如以下123 不会发出信号 */
    [[self.mTestField2.rac_textSignal ignore:@"123"] subscribeNext:^(id x) {
        @strongify(self);
        [self.mButton setTitle:x forState:UIControlStateNormal];

    }];

    //distinctUntilChanged 当上次一次信号的值和新的一次的值不一样的时候才会发出信号,常用于UI的刷新
    [[self.mTestField2.rac_textSignal  distinctUntilChanged] subscribeNext:^(id x) {
        @strongify(self);
        [self.mButton setTitle:x forState:UIControlStateNormal];
    }];

    /* * take :次数 发出限定次数后不在订阅 以下的方法实际只会看到textFiled输入一次后就不会改变了,默认执行1次,textfiled获取焦点执行一次,输入执行一次 */

    [[self.mTextFiled1.rac_textSignal take:3] subscribeNext:^(id x) {
        @strongify(self);
        [self.mButton setTitle:x forState:UIControlStateNormal];
    }];
    /** * takeLast 只取最后n次,但是前提是sendCompleted 就是必须知道这个信号发出几次后才能确定最后n次信号,是那几个信号 * 还有skip 跳过前 n次信号 switchToLatest 取最后一次信号发送,同样需要调用 sendCompleted */
    RACSubject *subject=[RACSubject subject];
    [[subject takeLast:1] subscribeNext:^(id x) {
        @strongify(self);
        [self.mButton setTitle:x forState:UIControlStateNormal];
    }];
    /** * 发送信号必须在订阅之后 */
    [subject sendNext:@"1"];
    [subject sendNext:@"2"];
    [subject sendNext:@"3"];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW,^{
        [subject sendCompleted];
    });

连接

/* * *concat 信号源1 连接 信号源2 信号1调用completed才会执行发送信号2 *可以使用then连接,原理是concat,concat实际是过滤信号2的发出的信号,当信号1结束只会才会执行信号源2发送的信号 * merge 合并信号(或|合并)只要有个信号源发出信号,就会执行订阅者的block * zipWith: 合并信号(且|合并) 两个信号同时发出才会执行,value是一个元组 */

    RACSignal *signal1=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"1"];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW,^{
            [subscriber sendCompleted];
        });
        return nil;
    }];

    RACSignal *singal2=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"2"];
        return nil;
    }];

    RACSignal *connect=[signal1 concat:singal2];

    [connect subscribeNext:^(id value) {
        NSLog(@"接受到信号%@",value);
    }];

合并

/* * comLast 合并最新的信号 reduce 聚合 */
    RACSignal *signal=[RACSignal combineLatest:
   @[
    [ self.mTextFiled1.rac_textSignal filter:^BOOL(NSString *value) {
        return value.length>2;
    }],[self.mTestField2.rac_textSignal filter:^BOOL(NSString *value) {
        return value.length>2;
    }]
    ]
    reduce:^id(NSString *v1,NSString *v2){//默认空参数,可以手动添加
       return [NSString stringWithFormat:@"%@-%@",v1,v2];
    }];
    RAC(self.mButton,backgroundColor)=[signal map:^id(NSString  *value) {
        NSLog(@"%@",value);
        return [value isEqualToString:@"123-123456"]?[UIColor blackColor]:[UIColor redColor];
    }];

Time相关

/* * timeout: 2 2后自动调用 error */
  [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      return nil;
  }] timeout:2 onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(id x) {
      NSLog(@"1.timeout next");
  } error:^(NSError *error) {
      //两秒后自定调用error
      NSLog(@"1.timeout error");
  }];
    /* delay:2 2秒执行block */
    [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        [subscriber sendNext:@"2"];
        return nil;
    }] delay:2] subscribeNext:^(id x) {
        NSLog(@"2s: %@",x);
    }];
    static int i=0;

    /* retry 只要sendError 就会重新执行一次block 直到成功 */
    [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        if(i>5){
            [subscriber sendNext:@"2"];
            [subscriber sendCompleted];
            NSLog(@"next i:%d",i);
        }else{
            [subscriber sendError:nil];
            NSLog(@"error i:%d",i);
            i++;
        }
        return nil;
    }] retry] subscribeNext:^(id x) {
        NSLog(@"---data:%@",x);
    }];

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom