iOS_RAC_02_ReactiveCocoa框架之常见对象介绍

使用RAC库之前的准备工作:

在5.0版本以后, RAC 拆分为四个库:

ReactiveCocoa、ReactiveSwift、

ReactiveObjC、ReactiveObjCBridge。

其中

ReactiveCocoa适用于纯Swift项目,

ReactiveObjC适用于纯OC项目。

 

若项目为Swift和OC混编,那么需要将ReactiveObjCReactiveCocoa都导入,

同时还需要手动导入ReactiveObjCBridge


打开命令行终端, 搜索reactiveobjc库:

touch podfile

open podfile

// pod search reactivecocoa

pod search reactiveobjc


// 如果搜索不到最新的reactivecocoa,就使用下面的命令,升级pod先

pod repo update

pod install


注意:

如果pod导入三方库后,头文件不显示的话,

解决办法: 

在TARGETS -> Build Settings -> Search Paths -> User Header Search Paths 中

写入 ${SRCROOT} 再将后面参数改为 recursive

就可以找到第3方库的头文件了.


纯OC代码的RAC库的 podfile示例:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, ‘8.0’

use_frameworks!



target “06RAC” do
    pod 'ReactiveObjC', '~> 3.0’

end


RAC常见对象之RACSignal信号 订阅者

RACSignal信号  与  订阅者

//
//  ViewController.m
//  06RAC
//
//  Created by beyond on 2017/12/12.
//  Copyright © 2017年 vwhm.net All rights reserved.


#import "ViewController.h"
#import "ReactiveObjC.h"
@interface ViewController ()
// 用一个成员变量,保存一下 信号内部创建的订阅者,防止信号被自动取消订阅(以便将来自己手动用RACDisposable取消订阅)
@property (nonatomic,strong) id <RACSubscriber> subscriber;
// 用一个成员变量,保存一下,以便将来自己手动用取消订阅
@property (nonatomic,strong) RACDisposable *disposable;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    
    // 1.创建信号时,block_didSubscribe代码并未执行(只有在订阅了信号之后,才会执行block_didSubscribe)
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        _subscriber = subscriber;
        
        // block_didSubscribe的作用是:描述当前信号的哪些数据需要发送
        // block_didSubscribe会被保存到RACDynamicSignal的成员变量里
        NSLog(@"sg_1_信号成功被激活,即将发送新的信号值");
        
        // 3.只有订阅者RACPassthroughSubscriber才能发送信号的新值
        [subscriber sendNext:@"新的信号值1"];
        NSLog(@"sg_3_发送信号完毕");
        
        return [RACDisposable disposableWithBlock:^{
            // 默认会自动来到这个代码,一般作为信号被取消订阅时的清理收尾工作
            
            // 注:但是,只要订阅者还在,就不会自动取消信号订阅(因此,要用成员变量保存一下subscriber)
            NSLog(@"sg_4_清理收尾");
        }];
    }];
    
    
    NSLog(@"sg_即将订阅信号");
    
    // 2.只有订阅信号后,默认的冷信号才会变成热信号!
    // nextBlockAfterValueChanged是当收到发送的信号改变后的新值时,要执行的代码
    // 作用:2.1.创建订阅者
    // 作用:2.2 .把nextBlockAfterValueChanged保存在了订阅者的成员变量里
    
    _disposable = [signal subscribeNext:^(id  _Nullable newValue) {
        // 只要[subscriber sendNext:@"新的信号值1"],就会调用这个nextBlockAfterValueChanged
        NSLog(@"sg_2_当订阅的信号发生改变时,要执行的代码_%@",newValue);
    }];
    
    
    // 手动取消信号订阅
    [_disposable dispose];
    
}







@end



/*
 *	信号类(RACSiganl),它本身不具备发送信号的能力,而是交给内部一个<订阅者>去发送新数据。
    默认一个信号都是冷信号,只有这个信号被订阅了,这个信号才会变为热信号。
 
 *	如何订阅信号:调用信号RACSignal的subscribeNext就能订阅了。
 */

// RACSignal使用步骤:
// 1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
// 2.订阅信号,就会激活信号. RACDynamicSignal的方法- (RACDisposable *)subscribeNext:(void (^)(id newValue))nextBlockAfterValueChanged
// 3.由订阅者 发送信号.RACPassthroughSubscriber的方法 - (void)sendNext:(id)value


// RACSignal底层实现:
// 1.创建信号时,首先把block_didSubscribe保存到信号(RACDynamicSignal)的成员变量中.

// 2.当信号被订阅,也就是调用RACDynamicSignal的方法- (RACDisposable *)subscribeNext:(void (^)(id newValue))nextBlockAfterValueChanged时.注意:nextBlockAfterValueChanged实际就是观察到信号改变后,要响应的操作
// 2.1 在上面的subscribeNext方法内部,会创建订阅者RACPassthroughSubscriber,并且把nextBlockAfterValueChanged保存到订阅者subscriber的成员变量中。
// 2.2 subscribeNext的方法内部会调用信号signal的成员变量block_didSubscribe,使信号变成了热信号.

// 3.在信号siganl的block_didSubscribe中执行[subscriber sendNext:@"signal_newValue"]方法,即可以告诉订阅者信号变化后的新值.
// 3.1 [subscriber sendNext:@"signal_newValue"]的底层于是会执行subscriber的成员变量nextBlockAfterValueChanged


RAC常见对象之信号提供者RACSubject:

RACSubject只能 先订阅信号,  然后才 发送信号

RACSubject使用示例:

//
//  ViewController.m
//  07RACSubject
//
//  Created by beyond on 2017/12/13.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // RACSubject既可以充当信号,又可以发送信号!因为继承自RACSignal并实现了RACSubscriber协议
    
    // 1.创建RACSubject信号
    RACSubject *subject = [RACSubject subject];
    
    // 2.订阅信号(RACSubject必须先订阅,再发送信号)
    // 不同的信号,处理订阅的方式不一样!!!
    // RACSubject处理订阅:仅仅是将创建的订阅者 到 成员变量数组里(subscribers)
    [subject subscribeNext:^(id  _Nullable newValue) {
        NSLog(@"sg_第1个订阅者_newValue:%@",newValue);
    }];
    
    // 注:因为是成员变量数组,所以可以多次订阅(会创建一个新的订阅者,并加入到成员变量数组里)
    [subject subscribeNext:^(id  _Nullable newValue) {
        NSLog(@"sg_第2个订阅者_newValue:%@",newValue);
    }];
    // 3.发送信号
    [subject sendNext:@"这个是要发送给订阅者的新信号值"];
    
    
    
    // 总结:核心思想,2点:
    // 底层实现:遍历成员数组里的所有订阅者,调用其nextBlock
    
    // 执行流程:
    
    // RACSubject被订阅,仅仅是保存创建的订阅者 到成员变量数组里(subscribers)
    // RACSubject发送数据,遍历成员变量数组里的所有的订阅者,然后调用他们的nextBlock
    
}


@end

运行效果:


RAC常见对象之RACReplaySubject

RACReplaySubject

既可以 先订阅信号 后 发送信号,

又可以  先发送信号 后  订阅信号

//
//  ViewController.m
//  08RACReplaySubject
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 0.RACReplaySubject可先发送信号,再订阅信号
    
    // 1.创建信号(RACReplaySubject新增了一个成员变量valuesReceived)
    RACReplaySubject *replaySubject = [RACReplaySubject subject];
    
    // 3.发送信号仅仅是:将newValue保存到valuesArray中;
    // 然后调用父类RACSubject的sendNext方法:遍历所有的订阅者,将新数据发送出去
    // 如果暂时还没有创建订阅者时(尚未被订阅),则不发送任何数据
    [replaySubject sendNext:@"这个是新值"];
    // 2.创建订阅者
    // 注:会遍历valuesArray中所有的值,让新建的订阅者,[subscriber sentNext:value](即:执行下面的block)
    // 目的就是:让该新建的订阅者获得valuesArray中所有的新值
    [replaySubject subscribeNext:^(id  _Nullable newValue) {
        NSLog(@"sg_newValue:%@",newValue);
    }];
}

@end


RACSubject 之 充当 代理 

storyboard如下:

RACSubject充当代理
RACSubject充当代理

RedView如下:

//
//  RedView.h
//  09RACSubjectDemo
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import <UIKit/UIKit.h>
#import "ReactiveObjC.h"
@interface RedView : UIView
// 强引用
@property (nonatomic,strong) RACSubject *btnClickSignal;
- (IBAction)btnClicked:(UIButton *)sender;
@end

//
//  RedView.m
//  09RACSubjectDemo
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import "RedView.h"

@implementation RedView
// 懒加载
- (RACSubject *)btnClickSignal
{
    if (_btnClickSignal == nil) {
        // 1.创建信号
        _btnClickSignal = [RACSubject subject];
    }
    return _btnClickSignal;
}

// 按钮点击
- (void)btnClicked:(UIButton *)sender
{
    // 3.发送信号
    [self.btnClickSignal sendNext:@"newValue"];
    
}
@end


控制器代码如下:

//
//  ViewController.h
//  09RACSubjectDemo
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import <UIKit/UIKit.h>
#import "RedView.h"

@interface ViewController : UIViewController
@property (nonatomic,weak) IBOutlet RedView *redView;

@end

//
//  ViewController.m
//  09RACSubjectDemo
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 订阅信号
    [_redView.btnClickSignal subscribeNext:^(id  _Nullable newValue) {
        NSLog(@"sg_%@",newValue);
    }];
}

@end


RAC常见对象之RACSequence集合类

model类

//
//  AnimeModel.h
//  10RAC集合类
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import <Foundation/Foundation.h>

@interface AnimeModel : NSObject
@property (nonatomic,copy) NSString *anime;
@property (nonatomic,copy) NSString *actress;
@end

控制器

//
//  ViewController.m
//  10RAC集合类
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"
#import "MJExtension.h"
#import "AnimeModel.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // [self tuple];
    // [self sequence_arr];
    // [self sequence_dic];
    [self sequence_dictArrToModelArr];
    
}
- (void)sequence_dictArrToModelArr
{
    // 将从Plist转化而来的字典数组,转成对象数组
    NSArray *dictArrFromPlist = @[
                                  @{@"anime":@"k-on",
                                    @"actress":@"平泽唯"
                                   },
                                  
                                  @{@"anime":@"龙与虎",
                                    @"actress":@"逢坂大河"
                                   },
                                  
                                  @{@"anime":@"未闻花名",
                                    @"actress":@"面码"
                                    },
                                  @{@"anime":@"狼与香辛料",
                                    @"actress":@"赫萝"
                                    },
                                  @{@"anime":@"俺妹",
                                    @"actress":@"黑猫"
                                    }
                                ];
    NSMutableArray *modelArr = [NSMutableArray array];
    // 方法1:遍历信号的每一个值(即dict)
    [dictArrFromPlist.rac_sequence.signal subscribeNext:^(NSDictionary *dict) {
        AnimeModel *model = [AnimeModel mj_objectWithKeyValues:dict];
        [modelArr addObject:model];
    }];
    
    // 方法2:直接mjextension
    modelArr = [AnimeModel mj_objectArrayWithKeyValuesArray:dictArrFromPlist];
    NSLog(@"sg_modelArr:%@",modelArr);
    
    // 方法3:sequence高级用法map
    NSArray *modelArr2 = [[dictArrFromPlist.rac_sequence map:^id _Nullable(NSDictionary *dict) {
        // block返回一个对象,从遍历中的字典转化而来的对象,重新放回到sequence集合里
        return [AnimeModel mj_objectWithKeyValues:dict];
        
        // 最后将转化完成的Sequence集合,转成NSArray
    }] array];
    NSLog(@"sg_modelArr2:%@",modelArr2);
    
    
    
    
    
    
    
    
}
- (void)sequence_dic
{
    // RAC中的集合类,用于统一数组和字典
    NSDictionary *dic = @{
                          @"k-on":@"平泽唯",
                          @"龙与虎":@"逢坂大河",
                          @"未闻花名":@"面码",
                          @"狼与香辛料":@"赫萝",
                          @"俺妹":@"黑猫"
                          };
    [dic.rac_sequence.signal subscribeNext:^(RACTwoTuple  *obj) {
//        NSString *key = obj[0];
//        NSString *value = obj[1];
//        NSLog(@"sg_遍历:%@_%@",key,value);
        
        
        // 或者使用:强大的宏
        RACTupleUnpack(NSString *key,NSString *value) = obj;
        NSLog(@"sg_遍历:%@_%@",key,value);
    }];
}
- (void)sequence_arr
{
    // RAC中的集合类,用于统一数组和字典
    NSArray *arr = @[@"k-on",@"大河",@"面码",@"赫萝",@"黑猫"];
    // 1.数组先转成sequence
    RACSequence *sequence = arr.rac_sequence;
    // 2.sequence再转成信号
    RACSignal *signal = sequence.signal;
    // 3.再订阅信号,则会自动遍历
    [signal subscribeNext:^(id  _Nullable obj) {
        NSLog(@"sg_遍历:%@",obj);
    }];
    
    
    // 综上所述
    [arr.rac_sequence.signal subscribeNext:^(id  _Nullable obj) {
        NSLog(@"sg_遍历2:%@",obj);
    }];
}

- (void)tuple
{
    // 类似于NSArray,只能放对象
    RACTuple *tuple = [RACTuple tupleWithObjects:@"N2",@"N1",@2018, nil];
    NSLog(@"sg_%@",tuple);
    NSLog(@"sg_%@",tuple[0]);
}




@end

podfile

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, ‘8.0’

use_frameworks!



target “10RAC集合类” do
    pod 'ReactiveObjC', '~> 3.0’
    pod 'MJExtension', '~> 3.0’

	
end


RAC的使用场景演示

storyboard如下:

RAC使用场景演示
RAC使用场景演示

控制器:

//
//  ViewController.h
//  11RAC场景
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import <UIKit/UIKit.h>
#import "RedView.h"
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet RedView *redView;
@property (weak, nonatomic) IBOutlet UIButton *btn;
@property (weak, nonatomic) IBOutlet UITextField *textField;


@end

//
//  ViewController.m
//  11RAC场景
//
//  Created by beyond on 2017/12/26.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"
#import "UIView+Frame.h"
// 注:rac_observeKeyPath的方法,默认情况下,并没有导入到主头文件中,需手动导入
#import "NSObject+RACKVOWrapper.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // 0.判断方法是否被执行
    // [self rac_signal_for_selector];
    // [self rac_signal_for_selector2];
    
    // 1.rac代替代理
    // [self rac_replace_delegate];

    // 2.rac代替KVO
    // [self rac_replace_kvo];
    // [self rac_replace_kvo2];
    
    // 3.rac监听事件,如按钮点击
    // [self rac_replace_controlEvent];
    
    // 4.rac代替通知
    // [self rac_replace_notification];
    
    // 5.监听文本框的文字
    [self rac_replace_textFieldListen];
    

    
}

- (void)rac_replace_textFieldListen
{
    // rac 监听 文本的改变
    [[_textField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
        NSLog(@"sg__textField has changed:%@",x);
    }];
}
- (void)rac_replace_notification
{
    // rac代替通知,将通知中心监听到的通知转成信号
    [[[NSNotificationCenter defaultCenter]rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable concretNotification) {
        /*
         sg_监听到了键盘将要弹出的通知:NSConcreteNotification 0x6000002562f0 {name = UIKeyboardWillShowNotification; userInfo = {
         UIKeyboardAnimationCurveUserInfoKey = 7;
         UIKeyboardAnimation Duration UserInfoKey = "0.25";
         UIKeyboard Bounds UserInfoKey = "NSRect: {{0, 0}, {320, 253}}";
         UIKeyboard CenterBegin UserInfoKey = "NSPoint: {160, 460}";
         UIKeyboard CenterEnd UserInfoKey = "NSPoint: {160, 441.5}";
         UIKeyboard FrameBegin UserInfoKey = "NSRect: {{0, 352}, {320, 216}}";
         UIKeyboard FrameEnd UserInfoKey = "NSRect: {{0, 315}, {320, 253}}";
         UIKeyboard IsLocal UserInfoKey = 1;
         }}
         */
        NSLog(@"sg_监听到了键盘将要 弹出 的通知:%@",concretNotification);
    }];
    
    
    
    // rac代替通知,将通知中心监听到的通知转成信号
    [[[NSNotificationCenter defaultCenter]rac_addObserverForName:UIKeyboardDidHideNotification object:nil] subscribeNext:^(NSNotification * _Nullable concretNotification) {
        /*
         sg_监听到了键盘将要关闭的通知:NSConcreteNotification 0x600000243ed0 {name = UIKeyboardDidHideNotification; userInfo = {
         
         UIKeyboard Bounds UserInfoKey = "NSRect: {{0, 0}, {320, 253}}";
         UIKeyboard CenterBegin UserInfoKey = "NSPoint: {160, 441.5}";
         UIKeyboard CenterEnd UserInfoKey = "NSPoint: {160, 694.5}";
         UIKeyboard FrameBegin UserInfoKey = "NSRect: {{0, 315}, {320, 253}}";
         UIKeyboard FrameEnd UserInfoKey = "NSRect: {{0, 568}, {320, 253}}";
         
         }}

         */
        NSLog(@"sg_监听到了键盘将要 关闭 的通知:%@",concretNotification);
    }];
    
    
}
- (void)rac_replace_controlEvent
{
    // rac监听按钮点击事件,将事件转化成signal
    [[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable btn) {
        // sg_监听到了按钮的controlEvent:<UIButton: 0x7fcb16507130; frame = (83.5 144; 153 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x610000232b20>>
        NSLog(@"sg_监听到了按钮的controlEvent:%@",btn);
    }];
}
- (void)rac_replace_kvo2
{
    // rac代替kvo,监听redView的frame发生新值的改变
    [[_redView rac_valuesForKeyPath:@"frame" observer:nil] subscribeNext:^(id  _Nullable newValue) {
        // 启动时就打印了一次sg_newValue:NSRect: {{67.5, 273.5}, {240, 120}}
        // touch屏幕时,又打印了一次sg_newValue:NSRect: {{0, 224}, {240, 120}}
        NSLog(@"sg_newValue:%@",newValue);
    }];
}
- (void)rac_replace_kvo
{
    // rac代替kvo,监听redView的frame发生新值的改变
    // 注意,此方法的缺点是:
    // rac_observeKeyPath的方法,默认情况下,并没有导入到主头文件中,需手动导入
    [_redView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
        // sg_NSRect: {{0, 224}, {240, 120}}
        // 注意:NS开头为NextStep,与Mac开发同源
        NSLog(@"sg_%@",value);
    }];
}


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    _redView.x = 0;
}
- (void)rac_signal_for_selector2
{
    // 控制器 监听redView中的按钮有没有被点击
    // 好处是:监听某个对象有没有调用某个方法
    // 坏处是:不能传递参数(要传递数据就用RACSubject)
    [[_redView rac_signalForSelector:@selector(btnClicked:)]subscribeNext:^(RACTuple * _Nullable x) {
        /*
         <RACTuple: 0x61800001b890> (
         "<UIButton: 0x7ff1b5e08570; frame = (137 269; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x608000038540>>"
         )
         */
        NSLog(@"sg_控制器 感知到了 redView中的btnClick_%@",x);
    }];
}
- (void)rac_signal_for_selector
{
    
    // 将方法订阅成信号,从而判断方法是否被触发(执行)
    // 好处是:监听某个对象有没有调用某个方法
    // 坏处是:不能传递参数(要传递数据就用RACSubject)
    [[self rac_signalForSelector:@selector(didReceiveMemoryWarning)] subscribeNext:^(RACTuple * _Nullable x) {
        // x是一个空的RACTuple
        NSLog(@"sg_控制器的didReceiveMemoryWarning方法被触发了_%@",x);
    }];
}

- (void)rac_replace_delegate
{
    // 参照RACSubject那个示例代码
}


@end


RAC常见对象之RACLiftSelector

RACLiftSelector 可以实现:  当多个请求完成后, 才执行某些操作

//
//  ViewController.m
//  12RAC多次请求
//
//  Created by beyond on 2017/12/27.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"
@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self rac_lift_selector];
}

- (void)rac_lift_selector
{
    RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        NSLog(@"sg_模拟网络操作1");
        
        [subscriber sendNext:@"sg_返回的网络数据1"];
        return nil;
    }];
    RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        NSLog(@"sg_模拟网络操作2");
        
        [subscriber sendNext:@"sg_返回的网络数据2"];
        return nil;
    }];
    
    NSArray *signalArr = @[signal1,signal2];
    // rac 实现 所有网络请求都完成的时候,才更新UI操作
    // 注意:所有请求完成时才执行的方法的 参数 必须 和信号的个数 一一对应
    [self rac_liftSelector:@selector(updateUIWithData1:andData2:) withSignalsFromArray:signalArr];
}
- (void)updateUIWithData1:(id)x1 andData2:(id)x2
{
    NSLog(@"sg_所有请求都完成时,才被执行_x1:%@,x2:%@",x1,x2);
}

@end


RAC常见宏演示

storyboard

RAC常见宏演示
RAC常见宏演示

主控制器

//
//  ViewController.m
//  13RAC常见宏
//
//  Created by beyond on 2017/12/27.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import "ViewController.h"
#import "ReactiveObjC.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UITextField *textField;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self rac_define_1];
    [self rac_define_2];
    [self rac_define_3];
    [self rac_define_4];
}
- (void)rac_define_4
{
    // 元组的包装和解包
    // 将参数们包装成元组
    RACTuple *tuple = RACTuplePack(@"龙与虎",@"逢坂大河");
    NSLog(@"sg_%@_%@",tuple[0],tuple[1]);
    
    // 将元组解包
    RACTupleUnpack(NSString *anime,NSString *actress) = tuple;
    NSLog(@"sg_%@_%@",anime,actress);
}
- (void)rac_define_3
{
    // 解决block的循环引用问题,下面两个配套使用,外面+block里面
    // 详细示例见NextViewCtrl
    // @weakify(self)
    // @strongify(self)
}

- (void)rac_define_2
{
    // 监听某个对象的某个属性,返回值为信号
    // 比如:监听label的文本,转化成信号
    [RACObserve(_titleLabel, text) subscribeNext:^(id  _Nullable newTextValue) {
        NSLog(@"sg_%@",newTextValue);
    }];
}
- (void)rac_define_1
{
    // 对某个对象的某个属性绑定
    // 比如:将label的文字 与 输入框的文本signal进行绑定
    RAC(_titleLabel,text) = _textField.rac_textSignal;
    
    
}

@end


循环引用演示

Next控制器(演示循环引用,nextViewCtrl消失时,dealloc未被执行)

//
//  NextViewCtrl.m
//  13RAC常见宏
//
//  Created by beyond on 2017/12/27.
//  Copyright © 2017年 vwhm.net All rights reserved.
//

#import "NextViewCtrl.h"
#import "ReactiveObjC.h"

@interface NextViewCtrl ()
@property (nonatomic,strong) RACSignal *signal;

@end

@implementation NextViewCtrl
- (IBAction)dismissBtnClicked:(id)sender {
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 演示 循环引用,出现内存泄露(控制器dismiss时dealloc没被调用)
    // [self rac_define_memoryleak];
    
    // 使用宏避免循环引用
    [self rac_define_avoidMemoryLeak];
    
    
}

// 使用宏避免循环引用
- (void)rac_define_avoidMemoryLeak
{
    // 先对self弱引用 (必须成对使用)
    // 相当于把self变成弱指针
    @weakify(self);
    
    // 双方强引用的时候,会出现内存泄露
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        // 再在代码块内强引用 (必须成对使用)
        // 对弱指针 又进行 强引用,目的是保证在代码块中不被销毁;强引用的范围仅限于代码块
        @strongify(self);
        
        
        // 经过上面的处理后,block中使用self已经不是一个强指针了
        NSLog(@"sg__ctrl的self已经不会出现循环引用了_%@",self);
        return nil;
    }];
    
    // self又对signal进行了强引用 (strong 属性的成员变量)
    _signal = signal;
}

// 演示 循环引用,出现内存泄露(控制器dismiss时dealloc没被调用)
- (void)rac_define_memoryleak
{
    // 双方强引用的时候,会出现内存泄露
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        
        // block中使用self,会对self进行了强引用
        NSLog(@"sg__ctrl被强引用,无法被销毁_%@",self);
        return nil;
    }];
    
    // self又对signal进行了强引用 (strong 属性的成员变量)
    _signal = signal;
}

- (void)dealloc
{
    NSLog(@"sg__dealloc__安全销毁__无内存泄漏");
}

@end


github地址

下载演示代码