阅读 1120
RAC(ReactiveCocoa)的基本使用

RAC(ReactiveCocoa)的基本使用

RAC简介

一. 什么是ReactiveCocoa?

ReactiveCocoa(其简称为RAC)是由Github 开源的一个应用于iOS和OS X开发的新框架。RAC具有函数式编程(FP)和响应式编程(RP)的特性。它主要吸取了.Net的 Reactive Extensions的设计和实现。

ReactiveCocoa 的宗旨是Streams of values over time ,随着时间变化而不断流动的数据流。

ReactiveCocoa 主要解决了以下这些问题:

  • UI数据绑定

    • UI控件通常需要绑定一个事件,RAC可以很方便的绑定任何数据流到控件上。
  • 用户交互事件绑定

    • RAC为可交互的UI控件提供了一系列能发送Signal信号的方法。这些数据流会在用户交互中相互传递。
  • 解决状态以及状态之间依赖过多的问题

    • 有了RAC的绑定之后,可以不用在关心各种复杂的状态,isSelect,isFinish……也解决了这些状态在后期很难维护的问题。
  • 消息传递机制的大统一

    • OC中编程原来消息传递机制有以下几种:Delegate,Block Callback,Target-Action,Timers,KVO,objc上有一篇关于OC中这5种消息传递方式改如何选择的文章Communication Patterns,推荐大家阅读。现在有了RAC之后,以上这5种方式都可以统一用RAC来处理。 其实也就是一下的这些消息传递机制。RAC都用了统一的方式封装起来让我们更加方便的去使用。

RAC的常用方法

1.通知NSNotificationCenter

  • 普通写法
    • 添加键盘弹出的通知addObserver
    [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillShow:)
                                                     name:UIKeyboardWillShowNotification
                                                   object:nil];
    复制代码
    • 实现方法
    - (void) keyboardWillShow:(NSNotification *)note {
        NSLog(@"键盘弹出了");
    }
    复制代码
    • 移除通知
    - (void)dealloc
    {
        [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil];
    }
    复制代码
  • RAC 写法
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
        NSLog(@"%@",x);
    }];
    复制代码
    • x的打印结果
    NSConcreteNotification 0x6000000c5170 {name = UIKeyboardWillShowNotification; userInfo = {
    UIKeyboardAnimationCurveUserInfoKey = 7;
    UIKeyboardAnimationDurationUserInfoKey = "0.25";
    UIKeyboardBoundsUserInfoKey = "NSRect: {{0, 0}, {390, 336}}";
    UIKeyboardCenterBeginUserInfoKey = "NSPoint: {195, 1012}";
    UIKeyboardCenterEndUserInfoKey = "NSPoint: {195, 676}";
    UIKeyboardFrameBeginUserInfoKey = "NSRect: {{0, 844}, {390, 336}}";
    UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 508}, {390, 336}}";
    UIKeyboardIsLocalUserInfoKey = 1;
    }}
    复制代码

2.KVO

  • 普通写法

    • 模型
    @interface TestModel : NSObject
    @property (assign, nonatomic) int age;
    @property (assign, nonatomic) int height;
    @end
    
    复制代码
    #import "KVOViewController.h"
    #import "TestModel.h"
    
    @interface KVOViewController ()
    @property (strong, nonatomic) TestModel *testModel1;
    @end
    
    @implementation KVOViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        self.testModel1 = [[TestModel alloc] init];
        self.testModel1.age = 1;
        self.testModel1.height = 11;
    
    
        // 给testModel1对象添加KVO监听 当KVO监听age属性变化
        NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
        [self.testModel1 addObserver:self forKeyPath:@"age" options:options context:@"123"];
    }
    
    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
    {
        self.testModel1.age = 20;
    
        self.testModel1.height = 30;
    }
    
    
    // 当监听对象的属性值发生改变时,就会调用
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
    }
    
    - (void)dealloc {
        [self.testModel1 removeObserver:self forKeyPath:@"age"];
        [self.testModel1 removeObserver:self forKeyPath:@"height"];
    }
    复制代码
    • RAC写法

    #define RACObserve(TARGET, KEYPATH) _RACObserve(TARGET, KEYPATH)

    [RACObserve(self.testModel1, age)subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
    复制代码

3.代理 Delegate

  • 普通写法
1. 遵守代理 <UITextFieldDelegate> 

2. 空间 @property (weak, nonatomic) IBOutlet UITextField *textField;


3. 代理的响应者 self.textField.delegate = self;


4.代理方法
- (void)textFieldDidBeginEditing:(UITextField *)textField {
    NSLog(@"开始编辑");
}
复制代码
  • RAC写法
    self.textField.delegate = self;
    
   [[self rac_signalForSelector:@selector(textFieldDidBeginEditing:) fromProtocol:@protocol(UITextFieldDelegate)] subscribeNext:^(RACTuple * _Nullable x) {
        NSLog(@"%@",x);
    }];
    
    
    <RACTuple: 0x600000dd9130> (
    "<UITextField: 0x127d0bf90; frame = (97 118; 220 34); text = ''; opaque = NO; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x6000001e6f70>; borderStyle = RoundedRect; background = <_UITextFieldSystemBackgroundProvider: 0x600000f8eb80: backgroundView=<_UITextFieldRoundedRectBackgroundViewNeue: 0x127d0e710; frame = (0 0; 220 34); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x600000f8ec60>>, fillColor=<UIDynamicModifiedColor: 0x6000001874e0; contrast = normal, baseColor = <UIDynamicSystemColor: 0x600001a9bb40; name = systemRedColor>>, textfield=<UITextField 0x127d0bf90>>; layer = <CALayer: 0x600000fe3ee0>>"
)
复制代码

4.Target-Action

  • 普通写法
[self.button addTarget:self action:@selector(Click:) forControlEvents:UIControlEventTouchUpInside];

- (void)Click:(UIButton *)sender {
    NSLog(@"按钮被点击");
}
复制代码
  • RAC
[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
    NSLog(@"%@",x);
}];


<UIButton: 0x15a50d9f0; frame = (101 240; 213 205); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x60000262fb60>>
复制代码

5.UITextField

  • RAC 写法:
[self.textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
    NSLog(@"%@",x);
}];
复制代码
当在self.textField输入文字h时,会实时打印出变化后的文字
2019-05-16 18:02:58.562309+0800 001---RAC[54530:4536864] h
2019-05-16 18:02:59.049225+0800 001---RAC[54530:4536864] hh
2019-05-16 18:02:59.288995+0800 001---RAC[54530:4536864] hhh
复制代码

6.手势

UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
self.label.userInteractionEnabled = YES;
[self.label addGestureRecognizer:tap];
[tap.rac_gestureSignal subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
    NSLog(@"%@",x);
}];
复制代码

7、数组和字典的遍历

  • 数组
TestModel *model1 = [[TestModel alloc] init];
model1.age = 1;
model1.height = 11;

TestModel *model2 = [[TestModel alloc] init];
model2.age = 2;
model2.height = 22;


TestModel *model3 = [[TestModel alloc] init];
model3.age = 3;
model3.height = 33;

NSArray *array = @[model1,model2,model3];
[array.rac_sequence.signal subscribeNext:^(TestModel * _Nullable x) {
    NSLog(@"%d",x.age);
}];
复制代码
  • 字典
NSDictionary *dict = @{@"name":@"凡几多",@"age":@"20",@"sex":@"男"};
[dict.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
    //元祖
    NSLog(@"%@",x);
    RACTwoTuple *tuple = (RACTwoTuple *)x;
    NSLog(@"key == %@ , value = %@",tuple[0],tuple[1]);
}];
复制代码

8、计时器

  • NSTimer
NSTimer *timer = [NSTimer timerWithTimeInterval:30.0 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES]; // 需要加入手动RunLoop,需要注意的是在NSTimer工作期间self是被强引用的
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes]; // 使用NSRunLoopCommonModes才能保证RunLoop切换模式时,NSTimer能正常工作。

停止计时器
- (void)stopTimer {
    if (_timer) {
        [_timer invalidate];
    }
}
复制代码
  • RAC
- 主线程
[[RACSignal interval:2.0 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * _Nullable x) {
    NSLog(@"%@",x);
    NSLog(@"%@",[NSThread currentThread]);

}];

- 子线程
[[RACSignal interval:2.0 onScheduler:[RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh name:@"abc"]] subscribeNext:^(NSDate * _Nullable x) {
    NSLog(@"%@",[NSThread currentThread]);
}];
复制代码

拓展

  • 数组便利是先执行Block 里面的在执行 block 外面的语句

参考文章 利用栅栏函数完成任务的调度

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    
    TestModel *model1 = [[TestModel alloc] init];
    model1.age = 1;
    model1.height = 11;
    
    TestModel *model2 = [[TestModel alloc] init];
    model2.age = 2;
    model2.height = 22;
    
    
    TestModel *model3 = [[TestModel alloc] init];
    model3.age = 3;
    model3.height = 33;
    
    NSArray *array = @[model1,model2,model3];
    
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent", DISPATCH_QUEUE_CONCURRENT);
    NSLog(@"🔞 START: %@", [NSThread currentThread]);
    dispatch_async(concurrentQueue, ^{
        [array.rac_sequence.signal subscribeNext:^(TestModel * _Nullable x) {
            NSLog(@"%@",[NSThread currentThread]);
            NSLog(@"%d",x.age);
        }];
    }); // ⬅️ 任务一
    dispatch_barrier_async(concurrentQueue, ^{ sleep(3); NSLog(@"🚥🚥 %@", [NSThread currentThread]);}); // ⬅️ Barrie 任务
    dispatch_async(concurrentQueue, ^{ NSLog(@"111");}); // ⬅️ 任务四
    NSLog(@"🔞 END: %@", [NSThread currentThread]);
    
}
复制代码
文章分类
iOS
文章标签