美文网首页
RactiveCocoa

RactiveCocoa

作者: 为什么划船不靠桨 | 来源:发表于2018-07-17 18:52 被阅读0次

RAC 指的就是 RactiveCocoa ,基于函数式响应式编程思想的Objective-C实践,是 Github 的一个开源框架,能够帮我们提供大量方便的事件处理方案,让我们更简单粗暴地去处理事件.使用MVVM框架就不得不提RAC.RAC具有高聚合低耦合的思想,使用RAC会让代码更简洁,逻辑更清晰。
RAC几乎接管了Apple所有的事件机制,由于RAC将Cocoa中KVO、UIKitEvent、delegate、selector等都增加了RAC支持,所以都不用去做很多跨函数的事。

ReactiveCocoa主要包含四个组件:
信号源:RACStream 及其子类;
订阅者:RACSubscriber 的实现类及其子类;
调度器:RACScheduler 及其子类;
清洁工:RACDisposable 及其子类。
而信号源是最核心的部分,其它所有组件都是围绕它运作的。
RAC 的核心思想:创建信号 - 订阅信号 - 发送信号

1. RACSignal 信号类

表示将来有数据传递,有数据改变,信号内部接收到数据,就会马上发出数据,外部就可以接收到数据了。默认信号都是冷信号,就是这个值改变了它不会触发,只有订阅(调用信号RACSignal的subscribeNext订阅)了这个信号,这个信号才会变为热信号(值一改变就触发),才会触发。

/* 创建信号 */
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //RACSubscriber订阅者对象,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方法才能成为订阅者。通过create创建的信号,都有一个订阅者,帮助他发送数据。
        /* 发送信号 */
        [subscriber sendNext:@"发送信号"];
        
        return [RACDisposable disposableWithBlock:^{
            // 当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。
            // 执行完Block后,当前信号就不在被订阅了。
            NSLog(@"dealloc");
        }];
        
        //return nil;
    }];
   //接收信号
    [signal subscribeNext:^(id  _Nullable x) {
        
    }];

也可以使用RACDisposable类直接取消订阅

/* 订阅信号 */
    RACDisposable *disposable = [signal subscribeNext:^(id  _Nullable x) {
        //RACDisposable用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。 当你不想监听某个信号时,可以通过它主动取消订阅信号。
        NSLog(@"信号内容:%@", x);
    }];
    
    /* 取消订阅 */
    [disposable dispose];
2. 定时器timer(在子线程执行)
    [[RACSignal interval:1.0 onScheduler:[RACScheduler scheduler]] subscribeNext:^(NSDate * _Nullable x) {
        NSLog(@"当前时间:%@", x); // x 是当前的系统时间
        //关闭计时器就是取消订阅
        //[disposable dispose];
    }];
3. RACSubject 信号提供者

RACSubject自己可以充当信号,又能发送信号。(和代理的用法类似,通常用来代替代理,有了它,就不必定义代理了)

/* 创建信号 */
    RACSubject *subject = [RACSubject subject];
    
    /* 订阅信号(通常在别的视图控制器中订阅,与代理的用法类似) */
    [subject subscribeNext:^(id  _Nullable x) {
        /*
         1创建订阅者对象
         2将block放到订阅者对象中
         3将订阅者对象放到subscribes数组里面
         */
        NSLog(@"信号内容:%@", x);
    }];
    
    /* 发送信号 */
    /*
     其内部的真实操作是遍历信号对象中的数组,取出订阅者,调用订阅者中的block执行
     */
    [subject sendNext:@"发送信号"];
4. RACTuple 元祖(和 OC 的数组其实是一样的,其实就是封装了我们 OC 的数组)
/* 创建元祖 */
    RACTuple *tuple1 = [RACTuple tupleWithObjects:@"1", @"2", @"3", @"4", @"5", nil];
    
    /* 从别的数组中获取内容 */
    RACTuple *tuple2 = [RACTuple tupleWithObjectsFromArray:@[@"1", @"2", @"3", @"4", @"5"]];
    
    /* 利用 RAC 宏快速封装 */
    RACTuple *tuple3 = RACTuplePack(@"1", @"2", @"3", @"4", @"5");
    
    NSLog(@"取元祖内容:%@", tuple1[0]);
    NSLog(@"第一个元素:%@", [tuple2 first]);
    NSLog(@"最后一个元素:%@", [tuple3 last]);

这里再说一下RACSequence这个集合类,用于代替NSArray, NSDictionary,可以使用它来快速遍历数组和字典。

//遍历数组
    /*
     遍历原理:
     通过arr.rac_sequence把数据arr转化成集合RACSequence
     通过arr.rac_sequence.signal把集合RACSequence转化成了信号
     *通过subscribeNext订阅信号,遍历集合
     */
    NSArray *array = @[@"1", @"2", @"3", @"4", @"5"];
    [array.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"数组内容:%@", x); // x 可以是任何对象
    }];


    /* 遍历字典 */
    //遍历字典,遍历出来的键值对会包装成RACTuple(元组对象)
    NSDictionary *dictionary = @{@"key1":@"value1", @"key2":@"value2", @"key3":@"value3"};
    [dictionary.rac_sequence.signal subscribeNext:^(RACTuple * _Nullable x) {
       //RACTupleUnpack是一个宏定义
        RACTupleUnpack(NSString *key, NSString *value) = x; // x 是一个元祖,这个宏能够将 key 和 value 拆开
        NSLog(@"字典内容:%@:%@", key, value);
    }];


//下面两个方法都是将数组内容全部换为 0 ,第一个是单个操作,第二个是一次性全部替换,两个方法都不会改变原数组内容,操作完后都会生成一个新的数组,省去了创建可变数组然后遍历出来单个添加的步骤。
    /* 内容操作 */
    NSArray *array1 = @[@"1", @"2", @"3", @"4", @"5"];
    NSArray *newArray1 = [[array1.rac_sequence map:^id _Nullable(id  _Nullable value) {
        
        NSLog(@"数组内容:%@", value);
        
        return @"0"; // 将所有内容替换为 0
        
    }] array];


    /* 内容快速替换 */
    NSArray *array2 = @[@"1", @"2", @"3", @"4", @"5"];
    NSArray *newArray2 = [[array.rac_sequence mapReplace:@"0"] array]; // 将所有内容替换为 0
5. 代理
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"标题" message:@"123456" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"other", nil];
//可以省去监听以及设置 delegate 的步骤
    [[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple *tuple) {
        NSLog(@"%@",tuple.first);
        NSLog(@"%@",tuple.second);
        NSLog(@"%@",tuple.third);
    }];
    [alertView show];
6. 通知
//RAC将收到通知后的方法封装成了block,省去了在 dealloc 中清除通知和监听通知创建方法的步骤。
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification *notification) {
        NSLog(@"%@", notification.name);
        NSLog(@"%@", notification.object);
    }];
7. KVO
    UIScrollView *scrolView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
    scrolView.contentSize = CGSizeMake(200, 800);
    scrolView.backgroundColor = [UIColor redColor];
    [self.view addSubview:scrolView];

    //原生,其缺点是当你需要观察很多属性时,你要写很多次下面的方法,而且在观察的方法中还要去判断KeyPath具体是什么
    [scrolView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];

    //RAC方式1  利用 RAC 的宏,只要对象的属性发生改变就会产生信号
    [RACObserve(scrolView, contentOffset) subscribeNext:^(id x) {
        NSLog(@"contentOffset: %@",x);
    }];
    //RAC方式2
    [self.redView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:self block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
        
    }];
    //RAC方式3    先包装成了一个信号,只要值改变就会发送信号。
    [[self.redView rac_valuesForKeyPath:@"frame" observer:self] subscribeNext:^(id  _Nullable x) {
        NSLog(@"frame属性的改变1:%@", x); // x 是监听属性的改变结果
    }];
8. 监听 TextField 的输入改变
//可以省去设置 delegate 和实现代理方法的步骤。
    [[self.textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
        
        return value.length > 5; // 表示输入文字长度 > 5 时才会调用下面的 block
        
    }] subscribeNext:^(NSString * _Nullable x) {
        
        NSLog(@"输入框内容:%@", x);
    }];
    
    [[self.textField.rac_textSignal map:^id(id value) {
        return [UIColor redColor];
    }] subscribeNext:^(id x) {
        //        NSLog(@"x:%@", x);
    }];
    
    
    [self.textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"输入框内容:%@", x);
    }];
    
    //给label同时赋值,其实就是用绑定信号的,只要产生信号内容,就会把内容给属性赋值
    UILabel *label;
    RAC(label,text) = self.textField.rac_textSignal;
9. 添加手势
//UIView
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init];
    [tap.rac_gestureSignal subscribeNext:^(id x) {
        NSLog(@"x:%@", x);
        self.redView.backgroundColor = [UIColor yellowColor];
    }];
    [self.redView addGestureRecognizer: tap];
10. 监听 Button 点击事件
//可以省去 addTarget 添加事件和创建方法的步骤。
    [[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        NSLog(@"点击了按钮");
    }];


    //登录按钮状态实时监听
    //下面表示只有 用户名 和 密码 输入框内容都大于 0 时,登录 按钮才可以点击,而且状态是实时监听的,一句代码就能完成这个功能。
    RAC(self.btn, enabled) = [RACSignal combineLatest:@[self.textField.rac_textSignal, self.textField.rac_textSignal] reduce:^id _Nullable(NSString * username, NSString * password){
        
        return @(username.length && password.length);
    }];

相关文章

网友评论

      本文标题:RactiveCocoa

      本文链接:https://www.haomeiwen.com/subject/lpcgpftx.html