RAC稍深入-适合新手学习RAC

继续完成RAC的坑


前言

经过上篇文章的学习,相信不少读者已经熟悉了RAC的基本用法,可以尝试在以后的开发中不用add target语句了。这篇文章将稍微深入一点了解RAC的更多功能以及适用场合。

什么是信号

RAC的核心就是信号,即RACSignal

信号可以看做是传递信号的工具,当数据变化时,信号就会发送改变的信息,以通知信号的订阅者执行方法。

热/冷信号

默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。

自己动手写一个RACSignal

//创建信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    [subscriber sendNext:@"signal"];
    [subscriber sendCompleted];
    return nil;
}];

//订阅信号
[signal subscribeNext:^(id x) {
    NSLog(@"x = %@", x);
} error:^(NSError *error) {
    NSLog(@"error = %@", error);
} completed:^{
    NSLog(@"completed");
}];

这是一个信号从创建到接收的完整过程,我们接下来看看控制台输出了什么。

2016-01-05 14:33:06.106 RACStudyTest[895:38671] x = signal
2016-01-05 14:33:06.106 RACStudyTest[895:38671] completed

可以看到,创建信号时我们sent了一个signal,在我们订阅subscribeNext时存储在x中的就是这个字符串signal。从这里看出来,不但我们可以给订阅者传递字符串,只要是一个类一个对象我们都可以传递。

另一方面控制台输出了completed说明订阅信号部分的completed块下得方法也被执行了,这是因为饿哦在创建时发送完signal后又发送了一个completed。同理,error下得方法我们也可以这样调用。

融汇贯通一下,我们之前学的所有RAC的用法就是一个创建信号订阅信号的过程!

信号的处理

map

这里的map不是地图,而是映射的意思,就是创建一个订阅者的映射并且返回数据,具体用法我们来看代码。

[[self.textFild.rac_textSignal map:^id(id value) {
    NSLog(@"%@", value);
    return @1;
}] subscribeNext:^(id x) {
    NSLog(@"%@", x);
}];

在TextFild控件中输入good,输出:

2016-01-05 15:02:15.180 RACStudyTest[1418:57245] 1
2016-01-05 15:02:21.710 RACStudyTest[1418:57245] g
2016-01-05 15:02:21.711 RACStudyTest[1418:57245] 1
2016-01-05 15:02:22.954 RACStudyTest[1418:57245] go
2016-01-05 15:02:22.955 RACStudyTest[1418:57245] 1
2016-01-05 15:02:23.464 RACStudyTest[1418:57245] goo
2016-01-05 15:02:23.464 RACStudyTest[1418:57245] 1
2016-01-05 15:02:23.705 RACStudyTest[1418:57245] good
2016-01-05 15:02:23.705 RACStudyTest[1418:57245] 1  

还是之前那个监听textfild编辑变化的例子,可以看到,当信号被订阅变成热信号后,这里的map构造的映射块value的值就是控件中的字符变化,而订阅者x的值就是映射者的返回值1。

根据这个功能我们就可以对我们监测的东西和我们需求的东西进行转换。比如监听了字符串变化,我们需要的时变化后的字符串长度而不是变化的字符串本身,则可以在map的返回值中返回text.length,就可以实时捕获到字符串长度;甚至做一个映射表,将各个变化进行一对一或者一对多或者多对一的处理。

filter

filter就是过滤,它可以帮助你筛选出你需要的信号变化。

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

输入goodnight,输出:

2016-01-05 15:18:20.492 RACStudyTest[1490:66721] x = good
2016-01-05 15:18:27.917 RACStudyTest[1490:66721] x = goodn
2016-01-05 15:18:28.129 RACStudyTest[1490:66721] x = goodni
2016-01-05 15:18:28.433 RACStudyTest[1490:66721] x = goodnig
2016-01-05 15:18:28.930 RACStudyTest[1490:66721] x = goodnigh
2016-01-05 15:18:29.155 RACStudyTest[1490:66721] x = goodnight

上述例子是在字符串长度大于3时才会输出变换后的字符串。

take/skip/repeat

take是获取,skip是跳过,这两个方法后面跟着的都是NSInteger。所以take 2就是获取前两个信号,skip 2就是跳过前两个。repeat是重复发送信号。

RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    [subscriber sendNext:@"1"];
    [subscriber sendNext:@"2"];
    [subscriber sendNext:@"3"];
    [subscriber sendNext:@"4"];
    [subscriber sendNext:@"5"];
    [subscriber sendCompleted];
    return nil;
}] take:2];

[signal subscribeNext:^(id x) {
    NSLog(@"%@", x);
}completed:^{
    NSLog(@"completed");
}];

这个demo只会输出前两个信号1和2还有完成信号completed,skip,repeat同理。

相似的还有takeLast takeUntil takeWhileBlock skipWhileBlock skipUntilBlock repeatWhileBlock都可以根据字面意思来理解。

delay

延时信号,顾名思义,即延迟发送信号。

RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    [subscriber sendNext:@"delay"];
    [subscriber sendCompleted];
    return nil;
}] delay:2];

NSLog(@"tag");

[signal subscribeNext:^(id x) {
    NSLog(@"%@", x);
}];

输出:

2016-01-05 15:27:55.908 RACStudyTest[1543:71256] tag
2016-01-05 15:27:58.102 RACStudyTest[1543:71256] delay

看时间可以发现订阅者延迟了2秒才收到信号打印出x的值。PS:还有0.2s误差是因为运行到不同代码行的时间差

throttle

节流,在我们做搜索框的时候,有时候需求的时实时搜索,即用户每每输入字符,view都要求展现搜索结果。这时如果用户搜索的字符串较长,那么由于网络请求的延时可能造成UI显示错误,并且多次不必要的请求还会加大服务器的压力,这显然是不合理的,此时我们就需要用到节流。

[[[self.textFild rac_textSignal] throttle:0.5] subscribeNext:^(id x) {
    NSLog(@"%@", x);
}];

加了节流管道,后面跟上了类型为NSTimeInterval的参数后,只有0.5S内信号不产生变化才会发送请求,这样快速的输入也不会造成多次输出。

distinctUntilChanged

网络请求中为了减轻服务器压力,无用的请求我们应该尽可能不发送。distinctUntilChanged的作用是使RAC不会连续发送两次相同的信号,这样就解决了这个问题。

[[[self.textFild rac_textSignal] distinctUntilChanged] subscribeNext:^(id x) {
    NSLog(@"%@", x);
}];   

timeout

超时信号,当超出限定时间后会给订阅者发送error信号。

RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    [[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{
        [subscriber sendNext:@"delay"];
        [subscriber sendCompleted];
    }];
    return nil;
}] timeout:2 onScheduler:[RACScheduler mainThreadScheduler]];

[signal subscribeNext:^(id x) {
    NSLog(@"%@", x);
} error:^(NSError *error) {
    NSLog(@"%@", error);
}];

输出:

2016-01-05 15:41:58.686 RACStudyTest[1604:76716] Error Domain=RACSignalErrorDomain Code=1 "The operation couldn’t be completed. (RACSignalErrorDomain error 1.)"

由于在创建信号是限定了延迟3秒发送,但是加了timeout2秒的限定,所以这一定是一个超时信号。这个信号被订阅后,由于超时,不会执行订阅成功的输出x方法,而是跳到error的块输出了错误信息。timeout在用RAC封装网络请求时可以节省不少的代码量。

ignore

忽略信号,指定一个任意类型的量(可以是字符串,数组等),当需要发送信号时讲进行判断,若相同则该信号会被忽略发送。

[[[self.textFild rac_textSignal] ignore:@"good"] subscribeNext:^(id x) {
    NSLog(@"%@", x);
}];

总结

这篇文章比上一篇稍稍深入了RAC的附加功能,但还有很多例如扁平映射flattenMap,Signal of Signals都没有提到,RAC的东西实在太多太多,我说的也仅仅是九牛一毛。我将继续研究RAC的深入用法,并发文给大家介绍。如果文中有什么错误,也希望大家给我指出来,我将万分感谢。也欢迎来我的博客看看。

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

推荐阅读更多精彩内容

  • RAC使用测试Demo下载:github.com/FuWees/WPRACTestDemo 1.ReactiveC...
    FuWees阅读 6,344评论 3 10
  • 标签: iOS RAC 概述 ReactiveCocoa是一个函数响应式编程框架,它能让我们脱离Cocoa AP...
    GodyZ阅读 7,497评论 16 97
  • 作为一个iOS开发者,你写的每一行代码几乎都是在相应某个事件,例如按钮的点击,收到网络消息,属性的变化(通过KVO...
    jiajia1118阅读 794评论 0 2
  • RAC支持的UI控件 RACCommand RACCommand类用于表示事件的执行,一般来说是在UI上的某些动作...
    花前月下阅读 2,730评论 0 5
  • 最近有位编辑找我,问我认识不认识魔云兽,我说我俩是好朋友呀!(我真的没吹牛,我俩真的是好朋友,不相信可以去问他,不...
    鼹鼠的土豆阅读 1,238评论 4 32