ReactiveCocoa学习笔记-RAC的基础与简单使用

什么是Functional Reactive Programming?
Functional Reactive Programming(以下简称FRP)是一种响应变化的编程范式

a = 2
b = 2
c = a + b  // c is 4
b = 3  // now what is the value of c?

没错,如果使用FRP的话c的值会随着b的值变化而改变,这叫做「响应式编程」。



ReactiveCocoa(以下简称RAC)是在iOS开发中对FRP的实现FRP的核心是信号,
在RAC中信号使用RACSignal表示

可以把信号想象成水龙头,只不过里面不是水,而是玻璃球(value),直径跟水管的内径一样,这样就能保证玻璃球是依次排列,不会出现并排的情况(数据都是线性处理的,不会出现并发情况)。水龙头的开关默认是关的,除非有了接收方(subscriber),才会打开。这样只要有新的玻璃球进来,就会自动传送给接收方。可以在水龙头上加一个过滤嘴(filter),不符合的不让通过,也可以加一个改动装置,把球改变成符合自己的需求(map)。也可以把多个水龙头合并成一个新的水龙头(combineLatest:reduce:),这样只要其中的一个水龙头有玻璃球出来,这个新合并的水龙头就会得到这个球。



下面就来一些简单的代码示例: 想获取```nameTextFielt```的```text```实时赋值给某一模型```username```变量: ```object-c RAC(self, username) = self.nameTextFielt.rac_textSignal; ``` 这是把```nameTextFielt```的信号**绑定**到```username```这个属性上来, 和这里是一样的: ``` [self.nameTextFielt.rac_textSignal subscribeNext:^(NSString* x) {         self.username = x;     }]; ``` 给```nameTextFielt```的信号添加一个订阅者,这样每当```text```的值变化的时候就会有信号产生,我们可以在block里拿到这个text的变化的值来做操作。

如果不是每个信号都会处理,比如我们只想知道当输入了6个字符之后的字符串,可以这样把信号过滤(filter)

[[self.nameTextFielt.rac_textSignal filter:^BOOL(NSString* value) {
        return value.length > 6;
    }] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];


我们还可以映射(map)信号,比如想把用户输入的字符串转换为NSNumber类型:

[[self.nameTextFielt.rac_textSignal map:^id(NSString* value) {
        return @(value.integerValue);
    }]subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];


当然,信号还能被合并(combine),在用户登录的时候如果希望当用户名和密码输入长度大于6之后登录按钮才能点击,反正则不能:

RACSignal *ableSignal = [RACSignal combineLatest:@[self.nameTextFielt.rac_textSignal, self.passwordTextFielt.rac_textSignal] reduce:^id(NSString *name,  NSString *pswd){
        return @( name.length>6 && pswd.length>6 );
    }];
RAC(self.loginButton, enabled) = ableSignal;


但是我们通常并不这么干,而是去用RACSignal去实例一个 RACCommand对象,并且为我们的登录按钮绑定该命令:

RACCommand *loginCommand = [[RACCommand alloc]initWithEnabled:ableSignal signalBlock:^RACSignal *(id input) {
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            //在这里做登录请求
            BOOL success = YES;
            if (success) { // 登录成功
                [subscriber sendNext:@(success)];
                [subscriber sendCompleted];
            }else{ // 登录失败
                [subscriber sendError:[NSError new]];
                [subscriber sendCompleted];
            }
            return nil;
        }];
    }];

self.loginButton.rac_command = loginCommand;

这么做还有个好处,当subscriber不进行sendCompleted,即网络请求未返回时,按钮处于不可点击状态,可以防止重复提交



但是,如果我们想拿到网络请求返回的结果怎么办?
在上个代码中,如果网络请求成功的话,可以把返回结果返回:

[subscriber sendNext:result];
[[self.loginButton.rac_command execute:nil]subscribeNext:^(id x) {
        // x 即为网络返回的数据
    }];
posted @ 2015-12-11 16:46  王修斌  阅读(200)  评论(0编辑  收藏  举报