RAC(ReactiveCocoa)的基本使用

RAC簡介

一. 什麼是ReactiveCocoa?

ReactiveCocoa(其簡稱爲RAC)是由Github 開源的一個應用於iOS和OS X開發的新框架。RAC具備函數式編程(FP)和響應式編程(RP)的特性。它主要吸收了.Net的 Reactive Extensions的設計和實現。編程

ReactiveCocoa 的宗旨是Streams of values over time ,隨着時間變化而不斷流動的數據流。數組

ReactiveCocoa 主要解決了如下這些問題:markdown

  • UI數據綁定框架

    • UI控件一般須要綁定一個事件,RAC能夠很方便的綁定任何數據流到控件上。
  • 用戶交互事件綁定async

    • RAC爲可交互的UI控件提供了一系列能發送Signal信號的方法。這些數據流會在用戶交互中相互傳遞。
  • 解決狀態以及狀態之間依賴過多的問題ide

    • 有了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)oop

    [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);
}];
複製代碼

七、數組和字典的遍歷

  • 數組
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]);
}];
複製代碼

八、計時器

  • 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 外面的語句

參考文章 利用柵欄函數完成任務的調度post

- (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]);
    
}
複製代碼
相關文章
相關標籤/搜索