MVVM. RACiOS开发记录iOS Developer

RAC常见用法(一)

2017-04-27  本文已影响345人  小冰山口

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

首先看一下打算介绍的知识点:
知识点大纲
然后, 就开始One by One了:
(一) RAC的集合:
- (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;

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

- (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);
    }];
}
- (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 -------- ******************/

- (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;
        }
    }];

实现效果:

发送验证码实现效果

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

代码

上一篇下一篇

猜你喜欢

热点阅读