ReactiveCocoa使用詳解01

ReactiveCocoa(簡稱爲RAC),是由Github開源的一個應用於iOS和OS開發的函數式響應式編程框架,它提供了一系列用來組合和轉換值流的 APIhtml

一. 什麼是響應式變成思想?

學習一個框架以前, 首先要了解這個框架的編程思想, 這裏在介紹響應式編程思想以前, 先介紹一下以前接觸過的編程思想ios

1.面向對象

  • 萬物皆對象
    • 是一類以對象做爲基本程序結構單位的程序設計語言
    • 典型的面向對象的編程語言有C++, C#, Java

2. 面向過程

  • 一種以過程爲中心的編程思想
  • C語言就是一門面向過程的語言

3. 鏈式編程思想

  • 是將多個操做(多行代碼)經過點號(.)連接在一塊兒成爲一句代碼,使代碼可讀性好
  • 鏈式編程特色:方法的返回值是block, block必須有返回值(自己對象),block參數(須要操做的值)
  • 典型框架:masonry框架。

4. 函數式編程思想

  • 萬物皆是流
    • 不須要考慮調用順序,只須要知道考慮結果
    • 相似於蝴蝶效應,產生一個事件,會影響不少東西,這些事件像流同樣的傳播出去,而後影響結果
    • 表明:KVO運用

5. 函數式編程思想

  • 是把操做盡可能寫成一系列嵌套的函數或者方法調用
  • 特色: 每一個方法必須有返回值(自己對象),把函數或者Block當作參數,block參數(須要操做的值)block返回值(操做結果)
  • 表明:ReactiveCocoa

6. ReactiveCocoa編程思想

  • 函數式編程 Functional Programming
  • 響應式編程 Reactive Programming

因此, ReactiveCocoa被描述爲函數響應式編程(FRP)框架, 下面具體介紹一下RAC的一些常見類git

二. RACSiganl 信號類

  • ReactiveCocoa 中最核心的概念之一就是信號RACStreamRACStream中有兩個子類——RACSignalRACSequence; 這裏咱們就主要說一下RACSignal;
  • ReactiveCocoa整個庫中,RACSignal佔據着比較重要的位置,而RACSignal的變換操做更是整個RACStream流操做核心之一
  • 下面讓咱們倆看一下RACSignal被訂閱的完整過程
- (void)test2 {
    //建立信號
    RACSignal *single = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //發送消息
        [subscriber sendNext:@"a"];
        [subscriber sendNext:@"b"];
        //發送完成
        [subscriber sendCompleted];
        
        //清空數據
        return [RACDisposable disposableWithBlock:^{
            //當訂閱者被消耗的時候就會執行
            //當訂閱者發送完成,或者error的時候也會執行
            NSLog(@"RACDisposable的block");
        }];
    }];
    
    //訂閱信號
    RACDisposable *disposable = [single subscribeNext:^(id  _Nullable x) {
        NSLog(@"value = %@", x);
    } error:^(NSError * _Nullable error) {
        NSLog(@"error: %@", error);
    } completed:^{
        NSLog(@"completed");
    }];
    
    //釋放
    [disposable dispose];
}

複製代碼
  • 在此以前先看一下RACSignal的一些子類
    • RACDynamicSignal: 動態信號,使用一個 block 來實現訂閱行爲,咱們在使用 RACSignal+createSignal: 方法時建立的就是該類的實例
    • RACEmptySignal:空信號,用來實現 RACSignal+empty 方法;
    • RACReturnSignal:一元信號,用來實現 RACSignal+return:方法;
    • RACErrorSignal:錯誤信號,用來實現 RACSignal+error: 方法;
    • RACChannelTerminal:通道終端,表明 RACChannel 的一個終端,用來實現雙向綁定

RACSignal在建立信號的時候,底層會調用RACDynamicSignalcreateSignal的方法, 以下:github

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
	return [RACDynamicSignal createSignal:didSubscribe];
}

複製代碼

這裏的block是一個 id<RACSubscriber> 類型的subscriber, 而這個RACSubscriber, 咱們能夠點進去看一些底層實現, 協議方法以下:編程

@protocol RACSubscriber <NSObject>
@required

/// Sends the next value to subscribers.
- (void)sendNext:(nullable id)value;

/// Sends the error to subscribers.
- (void)sendError:(nullable NSError *)error;

/// Sends completed to subscribers.
- (void)sendCompleted;

/// Sends the subscriber a disposable that represents one of its subscriptions.
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;

複製代碼
  • RACSignal底層實現:
    • 1.建立信號,首先把didSubscribe保存到信號中,還不會觸發。
    • 2.當信號被訂閱,也就是調用signalsubscribeNext:nextBlock
      • 2.1 subscribeNext內部會建立訂閱者subscriber,而且把nextBlock保存到subscriber中。
      • 2.2 subscribeNext內部會調用siganl的didSubscribe
      • 2.3 當信號訂閱完成, 不在發送數據的時候, 最好調用完成發送的[subscriber sendCompleted];
      • 訂閱完成的時候, 內部會自動調用[RACDisposable disposable]取消訂閱信號
    • 3.siganldidSubscribe中調用[subscriber sendNext:@1];
      • 3.1 sendNext底層其實就是執行subscribernextBlock

三. 信號提供者: RACSubject

  • 信號提供者,本身能夠充當信號,又能發送信號
  • 先訂閱, 在發送信號
  • 使用場景:一般用來代替代理/通知

1. RACSubject簡單使用

- (void)setRacSubject1 {
    //先訂閱, 在發送信號
    //1. 建立信號
    RACSubject *subject = [RACSubject subject];
    
    //2. 訂閱
    //內部建立RACSubscriber
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"第一個訂閱者--%@", x);
    }];
    
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"第二個訂閱者---%@", x);
    }];

    //3. 發送信號
    //遍歷全部的訂閱者, 執行nextBlock
    [subject sendNext:@2];
    
    /** 打印結果 2018-03-17 20:18:19.782119+0800 ReactiveObjc[23883:1420936] 第一個訂閱者--2 2018-03-17 20:18:19.784715+0800 ReactiveObjc[23883:1420936] 第二個訂閱者---2 */
}

複製代碼
  • RACSubject:底層實現和RACSignal不同
    • 1.調用subscribeNext訂閱信號,只是把訂閱者保存起來,而且訂閱者的nextBlock已經賦值了。
    • 2.調用sendNext發送信號,遍歷剛剛保存的全部訂閱者,一個一個調用訂閱者的nextBlock

2. RACReplaySubject簡單使用

  • 重複提供信號類,RACSubject的子類
  • 先發送信號,再訂閱信號;
  • 使用場景
      1. 若是一個信號每被訂閱一次,就須要把以前的值重複發送一遍,使用重複提供信號類。
      1. 能夠設置capacity數量來限制緩存的value的數量,即只緩充最新的幾個值
- (void)setReplaySubject {
    //建立信號
    RACReplaySubject *replySub = [RACReplaySubject subject];
    
    //發送信號
    [replySub sendNext:@23];
    [replySub sendNext:@34];
    
    //訂閱信號
    // 遍歷值,讓一個訂閱者去發送多個值
    // 只要訂閱一次,以前全部發送的值都能獲取到.
    [replySub subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@", x);
    }];
    
    /** 2018-03-19 12:01:14.112253+0800 ReactiveObjc[5130:446958] 23 2018-03-19 12:01:14.112511+0800 ReactiveObjc[5130:446958] 34 */
}
複製代碼
  • RACReplaySubject的底層實現
      1. 訂閱信號時,內部保存了訂閱者,和訂閱者響應block
      1. 當發送信號的,遍歷訂閱者,調用訂閱者的nextBlock
      1. 發送的信號會保存起來,當訂閱者訂閱信號的時,會將以前保存的信號,一個一個做用於新的訂閱者,保存信號的容量由capacity決定,這也是有別於RACSubject的

3. 替代代理/通知

  • 這裏咱們設想一個反向傳值的場景, vc裏面有一個自定義的view, 當點擊該View的時候, 更換vc的背景顏色
  • 一般咱們的作法是使用代理/通知/block

3-1. 下面看一下代理的簡單使用

在自定義View中設置協議緩存

#import <UIKit/UIKit.h>

@class SubjectView;
@protocol SubjectDelegate <NSObject>

@optional
- (void)viewWithTap:(SubjectView *)subView;

@end

@interface SubjectView : UIView

@property (nonatomic, weak) id<SubjectDelegate> delegate;


@end

複製代碼

在vc中, 遵循代理, 並實現代理方法框架

/// 代理方法
-(void)viewWithTap:(SubjectView *)subView{
    NSLog(@"完成代理, 點擊了view");
    
    UIColor *color = [UIColor colorWithRed:(arc4random() % 255) / 255.0 green:(arc4random() % 255) / 255.0 blue:(arc4random() % 255) / 255.0 alpha:1.0];
    self.view.backgroundColor = color;
}

複製代碼

3-1. RACSubject代替代理

在自定義SubjectView.h文件中dom

#import <UIKit/UIKit.h>
#import <ReactiveObjC.h>

@interface SubjectView : UIView

@property (nonatomic, strong) RACSubject *subject;

@end

複製代碼

在自定義SubjectView.m文件中編程語言

#import "SubjectView.h"

@implementation SubjectView

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    //發送信號
    [_subject sendNext:self];
}

@end

複製代碼

下面看一下在vc中的操做函數式編程

- (void)setupSubjectView {
    SubjectView *subV = [[SubjectView alloc]init];
    subV.backgroundColor = [UIColor redColor];
    subV.frame = CGRectMake(100, 100, 100, 100);
    RACSubject *subject = [RACSubject subject];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"完成代理, 點擊了view");
        
        UIColor *color = [UIColor colorWithRed:(arc4random() % 255) / 255.0 green:(arc4random() % 255) / 255.0 blue:(arc4random() % 255) / 255.0 alpha:1.0];
        self.view.backgroundColor = color;
    }];
    subV.subject = subject;
    [self.view addSubview:subV];
}
複製代碼
相關文章
相關標籤/搜索