响应式编程开发-ReactiveCocoa的使用方式

关注响应式编程开发-ReactiveCocoa

基本的使用方法

  例如,我们目前想要实现一个NSString对象可以一直绑定到最新的时间,即使字符串发生了变化,也不应该是再去使用时间去重新赋值了。

  听起来特别像Objective-C语言内的KVO特性,但是这并不是具有压倒性优势的那个方法:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;

  那么我们现在使用RAC框架来实现这个功能

***.h

//用来标识时间变量
@property (nonatomic ,strong) NSDate *time;
//用来标识文字显示区域
@property (nonatomic ,strong) IBOutlet UILabel *label;

  

***.m

    //申请注册一个每个1秒将会在主线程执行一次的信号量
    RACSignal *repeatSignal = [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] repeat];
    //为信号量添加执行代码端
    [repeatSignal subscribeNext: ^(NSDate* time){
        self.time = time;
    }];
    
    
    //申请注册一个时间属性的信号量
    RACSignal *timeSignal = [self rac_valuesForKeyPath:@"time" observer:self];
    //为信号量添加执行代码端
    [timeSignal subscribeNext:^(NSDate* time) {
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"HH:mm:ss"];
        
        self.label.text = [formatter stringFromDate:time];
        
        RELEASESAFELY(formatter);
    }];

  

  同样和Objective-C语言内的KVO特性不同的是,RACSignal信号量可以进行过滤设置。

  以上边的例子的话,我们加一个功能。

  •获取偶数秒的时间

  那么信号量部分的代码可以写为

    //申请注册一个时间属性的信号量
    RACSignal *timeSignal = [self rac_valuesForKeyPath:@"time" observer:self];
    //为信号量添加过滤block
    [[timeSignal filter:^BOOL(NSDate* time) {
        
        //获取描述的时间
        NSDateComponents *com = [[NSCalendar currentCalendar] components:NSCalendarUnitSecond fromDate:time];

        return com.second % 2 == 0;
        
    }] subscribeNext:^(NSDate* time) {
        NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
        [formatter setDateFormat:@"HH:mm:ss"];
        
        self.label.text = [formatter stringFromDate:time];
        
        RELEASESAFELY(formatter);
    }];

  

  信号量还可以用来导出对应的状态。与Objective-C语言KVO特性不同的是,RAC能够为新的值设置其他的属性。

那么我们还是举个功能例子

  •在注册用户时,当用户密码与确认密码相同时,在Label中显示"1",不相同时,显示"0";

  •如图所示

  响应式编程开发-ReactiveCocoa的使用方式_第1张图片

  传统方式代码

- (BOOL)isValid {
    return 
            [self.password.text length] > 0 &&
            [self.confirm.text length] > 0 &&
            [self.password.text isEqual:self.confirm.text];
}

#pragma mark - UITextFieldDelegate
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string
{

    self.label.text = @(self.isValid).description;
    return YES;
}

  我们发现逻辑被放在了很多方法里,零碎地摆放在view controller里,通过到处散布到delegate里的self.label.text = @(self.isValid).description;

方法在页面的生命周期中被调用。

  那么RAC实现方式的代码

    RACSignal *passworkSignal = self.password.rac_textSignal;
    RACSignal *confirmSignal = self.confirm.rac_textSignal;

    RACSignal *combineSignal = [RACSignal combineLatest:@[passworkSignal,confirmSignal] reduce:^(NSString *password, NSString *confirm){
        ;
        return @([password isEqualToString:confirm]).description;
    }];
    
    RAC(self,label.text) = combineSignal;

  所有对于的输入都整合在了一起。每次不论哪个输入框被修改了,用户的输入都会被reduce成一个字符串的值,然后就可以自动来控制注册按钮的可用状态了。

  RAC除了能够完成KVO的功能之外,同样可以完成按钮等用户响应的交互功能

  •完成一个点击按钮弹出Alert的功能

  •如图所示

  响应式编程开发-ReactiveCocoa的使用方式_第2张图片

  传统方式实现的代码

- (void)viewDidLoad {
    [super viewDidLoad];
    //添加触发事件
    [self.btn addTarget:self action:@selector(didClick) forControlEvents:UIControlEventTouchUpInside];
}
//点击按钮触发的回调方法
- (void)didClick
{
    //创建弹出窗口
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"蓝鸥" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
    [alertView show];
    
    RELEASESAFELY(alertView);
}

  RAC方式实现的代码如下

    //添加触发信号量
    self.btn.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        
        //创建弹出窗口
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"蓝鸥" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"确定", nil];
        [alertView show];
        
        RELEASESAFELY(alertView);
        
        return [RACSignal empty];
    }];

  

  通过以上的代码,RACSignal信号量具有如下功能

  • 异步控制或事件驱动的数据源:Cocoa编程中大多数时候会关注用户事件或应用状态改变产生的响应。

  • 链式以来操作:网络请求是最常见的依赖性样例,前一个对server的请求完成后,下一个请求才能构建。

  • 并行独立动作:独立的数据集要并行处理,随后还要把他们合并成一个最终结果。这在Cocoa中很常见,特别是涉及到同步动作时。

  RACSignal会触发它们的subscriber三种不同类型的事件:

  • 下一个事件从stream中提供一个新值。不像Cocoa集合,它是完全可用的,甚至一个signal可以包含 nil

  • 错误事件会在一个signal结束之前被标示出来这里有一个错误。这种事件可能包含一个 NSError 对象来标示什么发生了错误。错误必须被特殊处理——错误不会被包含在stream的值里面。

  • 完成事件标示signal成功结束,不会再有新的值会被加入到stream当中。完成事件也必须被单独控制——它不会出现在stream的值里面。

  一个RACSignal信号量的生命周期由很多下一个(next)事件和一个错误(error)完成(completed)事件组成(后两者不同时出现)。

总结对比

   RAC 与 KVO

  Key-Value Observing是Cocoa所有魔法的核心,它被广泛应用在ReactiveCocoa对于属性变化的影响动作中。然而KVO用起来即不简单也不开心:它的API有很多过度设计的参数,以及缺乏方便的block方式调用。

  RAC 与 Bindings

  Bindings也是黑魔法。

  虽然对OS X控制的要点就是Bindings,但是它的意义在近年来越来越没那么重要了,因为焦点已经移动到了iOS和UIKit这些Bindings不支持的东西身上。Bindings替代了大量的模版胶水代码,允许在Interface Builder中完成编码,但严格上说还是比较有局限性的,并且_无法_debug。RAC提供了一种简洁易懂、扩展性强的以代码为基础的API来运行在iOS上,目标就是取代所有在OS X能用Bindings实现的神奇功能。

 

  Objective-C在C的核心上吸收了Smalltalk的思想建立而成,但哲学理念上已经超越了它原本来源的血统。

  @protocol  是对C++多重继承的拒绝,顺应抽象数据的类型范式是对Java Interface的吸收。Objective-C 2.0引入了@property / @synthesize则灵感来自C#的 get; set; 方法对getter和setter的速记(就语法上来说,这也是NeXTSETP强硬路线坚持者经常辩论的一点)。Block给这门语言带来了函数式编程的好处,可以使用Grand Central Dispatch——来自Fortran / C / C++ standard OpenMP思想而成的基于队列的并发API。下标和对象字面量都是像Ruby、Javascript这样的脚本语言的标准特性,如今也由一个Clang插件被带入了Objective-C的世界里。

  ReactiveCocoa则给Objective-C带来了函数响应式编程的健康药剂。它本身也是受C#的Rx library、Clojure和Elm的影响发展而成。

好的点子会传染。ReactiveCocoa就是一种警示,提醒人们好的点子也可以从看似不太可能的地方传播过来,这样的新鲜思想对解决类似的问题也会有完全不同的方法呢。

你可能感兴趣的:(响应式编程开发-ReactiveCocoa的使用方式)