RAC常见用法(三)

本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.

本文将要介绍的RAC的常见用法大纲:
RAC常见用法(三)
  • RAC的映射:
- (RACSignal *)flattenMap:(__kindof RACSignal * _Nullable (^)(ValueType _Nullable value))block;
- (RACSignal *)map:(id _Nullable (^)(ValueType _Nullable value))block;

首先看flattenMap:这个方法, 其实这个方法的内部是绑定信号,

`flattenMap:`内部调用`bind:`

bind:方法是要返回一个block, 而flattenMap:这个方法是直接返回一个信号, 这个信号直接订阅就能够拿到包装了value的信号:

    RACSubject *subject = [RACSubject subject];
    
    [[subject flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
        return [RACReturnSignal return:value];
    }] subscribeNext:^(id  _Nullable x) {
        NSLog(@"拿到的数据是: %@ ", x);
    }];
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievantest"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            NSArray *dataArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:NULL];
            [subject sendNext:dataArray];
        }] resume];
    });

map信号就更为简单: 之前提到过, map:方法可以在block中返回任意类型的对象, 然后再订阅map:方法返回的信号, 就能够拿到这个对象:

    RACSubject *subject = [RACSubject subject];
    
    [[subject map:^id _Nullable(NSArray *value) {
        return [[value.rac_sequence map:^id _Nullable(id  _Nullable value) {
            return [KFCFood kfcFoodWithDictionary:value];
        }] array];
    }] subscribeNext:^(id  _Nullable x) {
        NSLog(@"拿到的结果是: %@ ", x);
    }];
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievantest"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            NSArray *resultArray = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:NULL];
            [subject sendNext:resultArray];
        }] resume];
    });
  • RAC的组合:

1.concat:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
       [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievan/signal1"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
           NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
           [subscriber sendNext:dataString];
           [subscriber sendCompleted];
       }] resume];
        return nil;
    }];
    
    RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievan/signal2"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            [subscriber sendNext:dataString];
        }] resume];
        return nil;
    }];
    
    [[signalA concat:signalB] subscribeNext:^(id  _Nullable x) {
        NSLog(@"result is = %@", x);
    }];
}

这种组合方式是用signalA去合并signalB

[signalA concat:signalB];

还有一种方法是类方法:

    [[RACSignal concat:@[ signalB, signalA, signalC]] subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@", x);
    }];

类方法是将信号放在一个数组里面, 调用顺序跟上面发送信号的顺序是不同的, 调用顺序是信号在数组中的顺序.

2.then:

    RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievan/signal1"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            [subscriber sendNext:dataString];
//            [subscriber sendCompleted];
        }] resume];
        return nil;
    }];
    
    [[signalA then:^RACSignal * _Nonnull{
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"http://mockhttp.cn/mock/tsaievan/signal2"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
                NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
                [subscriber sendNext:dataString];
                [subscriber sendCompleted];
            }] resume];
            return nil;
        }];
    }] subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@", x);
    }];

上面的代码执行结束后是不会打印任何东西的, 但是我把signalA中的这行代码打开

[subscriber sendCompleted];

打开之后, 就可以打印了:

运行结果

这说明, 必须signalA的信号接收到了, 并且发送信号结束之后, 才可以接收signalB, 且订阅组合信号的时候只接收signalB. 但它们之间是有依赖关系的,signalB的接收依赖于signalA!

3.merge:

这个方法和concat有些类似.

`merge`方法内部实现

先将需要合并的信号进行遍历, 放到一个可变数组里, 然后再返回一个信号,这个信号中已经做了sendCompleted的处理, 所以在merge:方法里就不需要再sendCompleted了, 而且, 其调用顺序也不再和数组中的顺序有关, 而是和sendNext的顺序有关.

4.zip

[[signalA zipWith:signalB] subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@", x);
    }];

zipWith:会把信号发送的内容打包成一个元祖 :

运行结果

同样的, 也有一个类方法zip:, 将信号数组打包:

    [[RACSignal zip:@[signalC, signalB, signalA]] subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@", x);
    }];

打包的顺序就是数组中的的顺序, 而不是信号sendNext:的顺序:

运行结果

5.combineLatest

+ (RACSignal *)combineLatest:(id<NSFastEnumeration>)signals reduce:(id (^)())reduceBlock;

这是一个类方法, 假设现在有这样一个需求,见下图:

账号密码同时存在,登录按钮才可以点击的需求

只需要下面的代码就可以搞定

- (void)viewDidLoad {
    [super viewDidLoad];
    RACSignal *combineSignal = [RACSignal combineLatest:@[_usernameTextField.rac_textSignal, _passwordTextField.rac_textSignal] reduce:^id(NSString *username, NSString *password){
        return @(username.length && password.length);
    }];
    
    RAC(_loginButton,enabled) = combineSignal;
}

关于reduceBlock:

The block which reduces the latest values from all the signals into one value. It must take as many arguments as the number of signals given. Each argument will be an object argument. The return value must be an object. This argument must not be nil.

这个block其实是带参数的, 参数的数目必须等同于信号的数目, 每一个参数都必须为OC对象, 返回值必须是对象(id类型), 这个block不能传空.

这个参数其实就是信号发送的值, 即sendNext:的值.

  • RAC的过滤

1.filter:

    [[_usernameTextField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
        return value.length > 3;
    }] subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"%@", x);
    }];

以上代码的意思就是, 当value.length大于3的时候, 才会返回YES, 这个时候才能信号才会发送, 订阅之后才能接收到数据.

2.ignore:

    RACSubject *subject = [RACSubject subject];
    [[[[subject ignore:@"1"] ignore:@"2"] ignore:@"5"] subscribeNext:^(id  _Nullable x) {
        NSLog(@"过滤后的数据为: %@ ", x);
    }];
    [subject sendNext:@"1"];
    [subject sendNext:@"2"];
    [subject sendNext:@"3"];
    [subject sendNext:@"4"];
    [subject sendNext:@"5"];

运行结果:

运行结果

可以看出的是: 过滤掉了1,2,5三个字符串. 但是不能将字符串装到数组里, 然后过滤数组中的所有元素, 只能像这样:

[[[subject ignore:@"1"] ignore:@"2"] ignore:@"5"];

链式编程的方式一个个忽略.

3.take:

    RACSubject *subject = [RACSubject subject];
    [[subject take:3] subscribeNext:^(id  _Nullable x) {
        NSLog(@"拿走的数据是 :%@ ", x);
    }];
    
    [subject sendNext:@"1"];
    [subject sendNext:@"2"];
    [subject sendNext:@"3"];
    [subject sendNext:@"4"];
    [subject sendNext:@"5"];

这个方法表示从前往后, 拿走前三个信号发送的值.

但是, 如果把代码改成这样:

    RACSubject *subject = [RACSubject subject];
    [[subject takeLast:3] subscribeNext:^(id  _Nullable x) {
        NSLog(@"拿走的数据是 :%@ ", x);
    }];
    
    [subject sendNext:@"1"];
    [subject sendNext:@"2"];
    [subject sendNext:@"3"];
    [subject sendNext:@"4"];
    [subject sendNext:@"5"];

却无法拿到最后三个值, 这是为什么呢? 因为你没有告诉信号, 你发送的数据是否结束, 所以, 它根本就不知道最后3个是哪3个, 所以必须加上代码:

[subject sendCompleted];

运行结果:

运行结果
    RACSubject *subject = [RACSubject subject];
   
    
    RACSubject *endSignal = [RACSubject subject];
    [[subject takeUntil:endSignal] subscribeNext:^(id  _Nullable x) {
        NSLog(@"endSignal发送信号后终止: %@ ", x);
    }];
    
    [subject sendNext:@"1"];
    [subject sendNext:@"2"];
    [endSignal sendNext:@"end"]; // [endSignal sendCompleted];也可以
    
    [subject sendNext:@"3"];
    [subject sendNext:@"4"];
    [subject sendNext:@"5"];
    [subject sendCompleted];

以上代码表示, 当某一个信号发送数据的时候, 拿这个信号发送前的数据. 所以以上代码运行之后, 只能拿字符串1和2:

运行结果

4.distinctUntilChanged
忽略重复数据:

    RACSubject *subject = [RACSubject subject];
    
    [[subject distinctUntilChanged] subscribeNext:^(id  _Nullable x) {
        NSLog(@"忽略掉重复的数据后得到的数据: %@ ", x);
    }];
    
    [subject sendNext:@"1"];
    [subject sendNext:@"1"];
    [subject sendNext:@"2"];
    [subject sendNext:@"2"];
    [subject sendNext:@"5"];
    [subject sendCompleted];

运行结果:

运行结果

5.skip:
跳过数据

    RACSubject *subject = [RACSubject subject];
    
    [[subject skip:3] subscribeNext:^(id  _Nullable x) {
        NSLog(@"跳过3个数据之后得到的数据: %@ ", x);
    }];
    
    [subject sendNext:@"1"];
    [subject sendNext:@"2"];
    [subject sendNext:@"3"];
    [subject sendNext:@"4"];
    [subject sendNext:@"5"];
    [subject sendCompleted];

运行结果:

运行结果

以上!
本文涉及到的代码下载 密码: ebrx

PS. 本人有若干成套学习视频, 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,858评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,372评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,282评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,842评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,857评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,679评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,406评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,311评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,767评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,945评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,090评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,785评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,420评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,988评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,101评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,298评论 3 372
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,033评论 2 355

推荐阅读更多精彩内容