RAC(ReactiveCocoa)

ReactiveCocoa是iOS开发的一个开源第三方框架,又被称作函数响应是编程框架。它结合了函数式编程响应式编程两种编程风格,定义了一个标准的接口来响应诸如点击按钮,网络请求完成等事件。

函数式编程&响应式编程

  • 何谓函数式编程

函数式编程就是把操作任务尽量写成一系列嵌套的函数或方法来调用。
这种编程思想有几个特点:方法的返回值是他本身,参数是Block,如果对block不太熟悉的同学,可以先看一下这篇文章:iOS Block,做一下了解。

  • 何谓响应式编程

我们可以用KVO来理解响应式编程。一个类中有一个user对象,user有“userId”“userName”两个属性,我们利用KVO来观察user的userName属性,当userName属性变化了的时候,会执行下面的方法。

#import "HomeViewController.h"
#import "User.h"

@interface HomeViewController ()
@property (nonatomic, strong) User *user;
@end

@implementation HomeViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.user = [User new];
    self.user.userName = @"张三";
    self.user.userId = 10086;
    [self.user addObserver:self forKeyPath:@"userName" options:
    |NSKeyValueObservingOptionNew context:nil];
        NSLog(@"%@", self.user);
        self.user.userName = @"李四";
        self.user.userId = 10010;
    }
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
       NSLog(@"%@%ld", self.user.userName, self.user.userId);
}

添加ReactiveCocoa到工程

和其他的第三方一样,添加ReactiveCocoa的方式有两种:

  • 通过CocoaPods添加
#RAC
pod 'ReactiveCocoa', '~> 2.5'
  • 通过GitHub手动下载ReactiveCocoa,根据文档添加到工程。

ReactiveCocoa常用的类

1. RACSignal(信号)

RACSignal是RAC中最核心的类,如果把它学懂了, 就已经可以用ReactiveCocoa进行开发了。

可以将RACSingnal当做一个传递信号的工具,当数据变化的时候,会将改变的信息发送出去,订阅者接受到这个信息,执行相应的方法。

信号我们将它分为两种:一种是冷信号,也是默认的信号,这种信号,即使数据改变了,也不会发送信息;当有订阅者订阅了这个信号,它才会变成热信号,当数据变化,它会将改变的数据发送出去,具体的,我们来看看RACSignal是怎么使用的:

    // 1 创建一个信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 3 发送数据
        [subscriber sendNext:@"第一个信号"]; 
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"取消信号");
        }];
    }];
    
    /*********** 到这里还是“冷信号” ***********/
    
    // 2 订阅信号(冷信号变热信号)
    [signal subscribeNext:^(id x) {
        // 4 处理信号
        NSLog(@"%@", x);
    }];

这里面写了RACSignal基本的操作,信号的创建,订阅以及发送。

2. RACSubscriber

上面的代码中我们还看到了两个类,在创建信号时block返回的类型 RACDisposable和参数 RACSubscriber。
RACSubscriber:表示订阅者的意思,在通过creat创建信号的时候,都会有一个订阅者,来发送数据。

3. RACDisposable

RACDisposable:是用来取消订阅、清理资源的。
这里我们还有一种写法,来手动取消订阅,这种情况,我们需要先来实例化个对象再来调用dispose方法:

    // 订阅信号
    RACDisposable *disposable = [signal subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];
    // 取消订阅
    [disposable dispose];
4. RACSubject & RACReplaySubject

RACSubject 是信号提供者,它既可以充当信号,又可以发送信号

    // 1创建信号
    RACSubject *subject = [RACSubject subject];
    // 2订阅信号
    [subject subscribeNext:^(id x) {
        // 4处理信号
        NSLog(@"订阅:%@", x);
    }];
    // 3发送信号
    [subject sendNext:@"信号"];

RACReplaySubject:是RACSubject的子类,重复提供信号。

    // 创建信号
    RACReplaySubject *replaySubject = [RACReplaySubject subject];
    // 发送信号
    [replaySubject sendNext:@"信号1"];
    // 订阅信号
    [replaySubject subscribeNext:^(id x) {
                NSLog(@"订阅1:%@", x);
    }];
    // 发送信号
    [replaySubject sendNext:@"信号2"];
    // 订阅信号
    [replaySubject subscribeNext:^(id x) {
                NSLog(@"订阅2:%@", x);
    }];

RACReplaySubject和RACSubject的最大的区别在于:
RACReplaySubject既可以先发送信号再订阅,也可以先订阅信号,再发送;而RACSubject只能先订阅信号,再发送。

5.RACTuple(元组)

在Objective-C中,本身是没有元组的,在Swift中有元组的概念。它与数组类似,可以以数组来初始化:

    NSArray *arr = @[@"张三", @"10086", @"boy"];
    RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:arr];
    NSLog(@"\nname:%@\nid:%@\ngender:%@", [tuple objectAtIndex:0], [tuple objectAtIndex:1], [tuple objectAtIndex:2]);
执行结果

ReactiveCocoa常用的方法

1. 监听事件

// 1. 监听事件
    [[self.bt_Go rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        NSLog(@"被点击");
        HomeViewController_2 *vc_2 = [[HomeViewController_2 alloc] initWithNibName:@"HomeViewController_2" bundle:nil];
        vc_2.hidesBottomBarWhenPushed = YES;
        [self.navigationController pushViewController:vc_2 animated:YES];

    }];

2. 监听通知

// 2. 监听通知
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"socketMessage" object:nil] subscribeNext:^(id x) {
        NSLog(@"收到!");
    }];

3. 监听TextField的变化

// 3. 监听TextField变化
    [self.tx_userName.rac_textSignal subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];

4. 监听变量属性(KVO)

// 4. 监听变量属性
    self.user = [User new];
    self.user.userId = 10086;
    self.user.userName = @"张三";
    [[self.user rac_valuesForKeyPath:@"userName" observer:nil] subscribeNext:^(id x) {
        NSLog(@"用户的名字改变了:%@", x);
    }];
    self.user.userName = @"李四";

5. 充当代理

// 5. 充当代理
    self.headerView = [[[NSBundle mainBundle] loadNibNamed:@"HomeHeaderView" owner:self options:nil] lastObject];
    self.headerView.frame = CGRectMake(0, SCREEN_HEIGHT - 300, SCREEN_WIDTH, 300);
    [self.view addSubview:self.headerView];
    [self.headerView.subject subscribeNext:^(User *user) {
        NSLog(@"收到信号:\n用户名:%@,用户id:%ld", user.userName, user.userId);
    }];

ReactiveCocoa常用的宏

1. RAC(TARGET, ...)

1.1 用途:

用来将某个对象的某个属性和某个信号绑定。当有信号过来,会自动赋值给对象的属性。

1.2 用法:
// lb_name 是一个UILable, tx_userName是UITextField
RAC(self.lb_name, text) = self.tx_userName.rac_textSignal;
1.3 对比:

我们实现以下,在一个textfield中输入内容时,将它实时输入到一个label中,看看以下几种方法是怎么实现的,哪个更简单。

  • 不用RAC,通过给TextField添加方法来实现
- (void)viewDidLoad {
    [super viewDidLoad];
    [self.tx_userName addTarget:self action:@selector(showtextFiledContents) forControlEvents:UIControlEventEditingChanged];
}
- (void) showtextFiledContents{
    self.lb_label1.text = self.tx_userName.text;
}

  • 利用RAC绑定属性和信号
    [self.tx_userName.rac_textSignal subscribeNext:^(NSString *x) {
        self.lb_label1.text = x;
    }];
  • 用RAC(TARGET, ...)实现
    RAC(self.lb_label1, text) = self.tx_userName.rac_textSignal;

2. RACObserve(TARGET, KEYPATH)

2.1 用途:

监听某个对象的某个属性,实际上它是[target_ rac_valuesForKeyPath:@keypath(TARGET, KEYPATH) observer:self]这句代码的宏

2.2 用法
    [RACObserve(self.user, userName) subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];
2.3 对比

实现监听user的userName属性

  • 不使用 RACObserve,直接监听
    self.user = [User new];
    self.user.userId = 10086;
    self.user.userName = @"张三";

    [[self.user rac_valuesForKeyPath:@"userName" observer:nil] subscribeNext:^(id x) {
        NSLog(@"用户的名字改变了:%@", x);
    }];
    
    [[self.bt_Go rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        self.user.userName = @"李四";
    }];
  • 使用 RACObserve
    self.user = [User new];
    self.user.userId = 10086;
    self.user.userName = @"张三";
        
    [RACObserve(self.user, userName) subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];
    
    [[self.bt_Go rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        self.user.userName = @"李四";
    }];

3. @weakify(Obj)和@strongify(Obj)

3.1用途:

防止循环引用

3.2 用法:

@weakify(Obj)和@strongify(Obj)是配合使用的,@weakify(Obj)写在block外,@strongify(Obj)写在block内

@weakify(self)
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        
        @strongify(self)
        [subscriber sendNext:self];
        return nil;
    }];
    
    [signal subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];

4. RACTuplepack

用途:

把数据包装成RACTuple(元组类)

用法:
RACTuple *tuple = RACTuplePack(@"123",@1);

5. RACTupleUnpack

用途:

把RACTuple(元组类)解包成对应的数据

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

推荐阅读更多精彩内容

  • 1.ReactiveCocoa简介 ReactiveCocoa(简称为RAC),是由Github开源的一个应用于i...
    清蘂翅膀的技术阅读 1,976评论 0 1
  • //联系人:石虎QQ:1224614774昵称:嗡嘛呢叭咪哄 一、RAC内存管理 RAC会维护一个全局的信号集合,...
    石虎132阅读 796评论 1 6
  • ‘’哎呀哎呀哎呀哎呀‘’,这一连串的叫声,从一个小学生的嘴里发出。她那弯弯的眉毛下,长着一颗水汪汪的大眼睛,高挺的...
    笑笑汤姆猫阅读 179评论 2 2
  • 很高兴能跟大家认识,也很感谢大家能来到这个课堂上,你们是幸运的。同时,你们又是不幸的。 幸运的是,你选择了...
    流年锦绣阅读 218评论 0 0