RAC常见用法(一)

作者: 小冰山口 | 来源:发表于2017-04-27 21:27 被阅读345次

    上次体验了一把RAC, 今天, 再介绍一下RAC的简单用法:

    首先看一下打算介绍的知识点:
    知识点大纲
    然后, 就开始One by One了:
    (一) RAC的集合:
    • 在RAC中, 也有一个元祖类, 叫做RACTuple, 它完全可以当做OC的数组来用, 比如:
    - (void)demo1 {
        /* 元祖 */
        RACTuple *tuple = [RACTuple tupleWithObjects:@"firstObject",@"secondObject",@1, nil];
        NSString *string = tuple[1];
        NSLog(@"%@", string);
    }
    

    你同样可以使用以下方法来创建一个元祖:

    /// Creates a new tuple out of the array. Does not convert nulls to nils.
    + (instancetype)tupleWithObjectsFromArray:(NSArray *)array;
    
    /// Creates a new tuple out of the array. If `convert` is YES, it also converts
    /// every NSNull to RACTupleNil.
    + (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert;
    
    /// Creates a new tuple with the given objects. Use RACTupleNil to represent
    /// nils.
    + (instancetype)tupleWithObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION;
    
    

    元祖完全可以当做数组来使用, 你也可以通过下标来取出元祖中的数据.

    • 在RAC中, 还有一个非常重要的集合类, 那就是RACSequence, 且看:
    - (void)demo2 {
        NSArray *array = @[@"jack", @"rose", @"james"];
        [array.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@", [NSThread currentThread]);
            NSLog(@"%@", x);
        }];
    }
    

    遍历一个数组, 打印的结果是:


    数组遍历打印结果

    可以看到的是: 数组中的元素被一一打印了, 值得注意的是: 这个打印的过程是在子线程调用的, 因为:

    + (RACScheduler *)schedulerWithPriority:(RACSchedulerPriority)priority name:(NSString *)name {
        return [[RACTargetQueueScheduler alloc] initWithName:name targetQueue:dispatch_get_global_queue(priority, 0)];
    }
    

    在这个方法的调用中, 使用了全局并发队列.

    同样的, 还有字典的遍历:

    - (void)demo3 {
        NSDictionary *dict = @{
                               @"name" : @"jack",
                               @"age" : @18,
                               };
        [dict.rac_sequence.signal subscribeNext:^(RACTuple * _Nullable x) {
            NSLog(@"%@", x);
    
        }];
    }
    

    可以看到打印结果是打印了两个元祖类型的对象:

    打印了两个元祖类型的对象

    那么, 如何将这个元祖类型的对象转化成字典的键值对一一输出呢? 这里就需要用到一个功能强大的宏, 在RAC中, 有很多很强大的宏, 这个宏是这样的:
    RACTupleUnpack(...)

    我们将需要解析的元祖赋值给这个宏, 同时, 将要解析的key值和value值做为宏的参数, 如下面的代码所示, 我们就能拿到字典的键值对了:

    - (void)demo3 {
        NSDictionary *dict = @{
                               @"name" : @"jack",
                               @"age" : @18,
                               };
        [dict.rac_sequence.signal subscribeNext:^(RACTuple * _Nullable x) {
            RACTupleUnpack(NSString *key, NSString *value) = x;
            NSLog(@"%@----%@",key,value);
        }];
    }
    
    • 还有一点需要单独拿出来讲的就是利用RAC进行字典转模型. RAC提供了一个方法, 可以直接将字典数组映射成模型数组:
    - (void)demo4 {
        NSURLSession *session = [NSURLSession sharedSession];
        [[session dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievantest"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            NSArray *array = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:NULL];
            NSArray *infoArr = [[array.rac_sequence.signal map:^id _Nullable(NSDictionary *value) {
                return [KFCFood kfcFoodWithDictionary:value];
            }] toArray];
            NSLog(@"%@", infoArr);
        }] resume];
    }
    

    当我们拿到服务器返回的响应体数据时, 利用json反序列化得到一个字典数组, 然后将数组转化成RACSequence对象,利用其signal属性进行映射, 再调用toArray方法就可以拿到装满模型对象的数组了:

    模型数组
    (二) RAC基本用法:
    • 代替代理:
    需求

    假设有如上的需求, 我们一般使用代理来完成. 那么现在不用了, RAC很容易就能解决这个问题:

    #import "YFSmallView.h"
    @implementation YFSmallView
    - (IBAction)greenButtonDidClick:(UIButton *)sender {
        NSLog(@"绿色按钮被点击了");
    }
    @end
    

    我在蓝色View内部定义一个处理点击事件的方法, 然后在控制器中, 响应这个方法产生一个信号, 然后订阅信号, 打印结果是一个元祖:

    - (void)demo1 {
        SEL sel = NSSelectorFromString(@"greenButtonDidClick:");
        [[_blueView rac_signalForSelector:sel] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@" ,x);
        }];
    }
    
    代替代理

    我们可以看到的是:元祖里面保存的是响应的方法的参数, 那么我们可以用解包的方法, 将参数提取出来:

    - (void)demo1 {
        SEL sel = NSSelectorFromString(@"greenButtonDidClick:");
        [[_blueView rac_signalForSelector:sel] subscribeNext:^(id  _Nullable x) {
            RACTupleUnpack(UIButton *sender) = x;
            self.view.backgroundColor = sender.backgroundColor;
        }];
    }
    

    通过这个, 我们能够拿到参数sender, 将sender的背景色赋值给控制器view :

    运行结果
    • 代替KVO:
        /****************** -------- 代替KVO -------- ******************/
    
    - (void)demo2 {
        [_blueView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
            NSLog(@"%@----%@", value, change);
        }];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
        static int x = 50;
        x++;
        _blueView.frame = CGRectMake(x, 50, 100, 100);
    }
    

    每点击屏幕就改变_blueView的值, 这时候, 就能够监听的到, 并且在block中进行逻辑处理, 参数value就是当前的keyPath属性对应的值, 参数change是一个字典,

    打印结果

    不过, 有更方便的监听方法:

    [[_blueView rac_valuesForKeyPath:@"frame" observer:self] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@", x);
        }];
    

    此时, 打印的x的值是当前属性的值.

    • 监听事件:
      以前, 我们监听按钮的点击事件, 我们得这么写代码:
    - (void)demo3 {
        [_blueButton addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
    }
    
    - (void)buttonClick:(UIButton *)sender {
        NSLog(@"点击了蓝色按钮+++%@", sender);
    }
    

    这样的不好的地方是, 业务逻辑被分割到了另外的地方,显得不统一, 而使用RAC的方法, 我们将逻辑直接写在block代码块里面, 这样可读性更强,使用更方便:

        /****************** -------- 监听事件 -------- ******************/
    - (void)demo3 {
        [[_blueButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
            NSLog(@"点击了蓝色按钮---%@", x);
        }];
    }
    
    • 代替通知:
        /****************** -------- 代替通知 -------- ******************/
    - (void)demo4 {
        [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
            NSLog(@"%@", x);
        }];
    }
    

    block中打印的就是通知本身:

    通知信息
    • 监听文本框
        /****************** -------- 监听文本框 -------- ******************/
    - (void)demo5 {
        [self.textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
            _textLabel.text = x;
        }];
    }
    

    实现效果:

    监听文本框实现效果
    (三) 利用RAC定时器创建制作一个发送验证码的小demo

    发送验证码是很常见的需求, 点击发送之后, 开始60秒倒计时, 此时按钮不能点击, 且文字变成灰色, 等到60秒时间到后, 便可以重新发送.一般情况下, 我们会用NSTimer, 但RAC为我们提供了更好的定时器方法:

    + (RACSignal *)interval:(NSTimeInterval)interval onScheduler:(RACScheduler *)scheduler;
    

    需要注意的是, 我们必须在主线程更新UI, 所以scheduler必须是主线程的scheduler, 使用[RACScheduler mainThreadScheduler]这个单例对象作为参数. 主要的代码如下:

    self.disposable = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
            _time--;
            if (_time > 0) {
                _sendLabel.text = [NSString stringWithFormat:@"已发送,请等待%@秒", @(_time)];
                _sendLabel.textColor = [UIColor lightGrayColor];
                [_sendLabel removeGestureRecognizer:tap];
            }else {
                _sendLabel.text = @"重新发送";
                _sendLabel.textColor = [UIColor whiteColor];
                [_sendLabel addGestureRecognizer:tap];
                [_disposable dispose];
                _time = 10;
            }
        }];
    

    实现效果:

    发送验证码实现效果

    我把涉及到的代码放到下面供大家参考:

    代码

    相关文章

      网友评论

        本文标题:RAC常见用法(一)

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