IOS RAC(Reactive Cocoa)

2020-08-07  本文已影响0人  越天高

导入RAC 使用CocoaPods直接导入 pod 'ReactiveObjC'

万事万物皆信号,任何事情都是通过信号传递的

信号 => 订阅 响应式编程思想,只要信号已发生改变,就会通知订阅

RACSignal

RACSignal:信号,ReactiveCocoa最基本类
RACDisposable:处理数据,清空数据
block:什么时候执行,block能干嘛
创建一个基本的信号

// 创建信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber)
    {
        //<1>
        //需要知道这个block:什么时候执行,block能干嘛
        NSLog(@"RACSignal执行block");
        // 网络请求
        
        [subscriber sendNext:@3];
        //<>
        [subscriber sendCompleted];
        
        
        //block 返回一个 RACDisposable对象
        return [RACDisposable disposableWithBlock:^
        {
            // 当订阅者被销毁的时候就会执行
            // 订阅发送完成或者error,也会执行Block
            // 清空数据
            NSLog(@"执行disposable");
        }];
    }];

RACSubscriber:订阅者,发送信号消息
信号本身不具备发送消息能力

先订阅 在发送消息
订阅就会执行RACSignal的block

    //需要了解信号什么时候发出,
    //订阅信号
    // 底层:创建订阅者
    [signal subscribeNext:^(id  _Nullable x)
    {
        NSLog(@"信号传值的时候 %@",x);
        //<2>
    }];
    //订阅信号的错误<3>->执行Signal的block 如果有发送消息就会醒下面
    [signal subscribeError:^(NSError * _Nullable error)
    {
        NSLog(@"error");
    }];
    //订阅信号的完成<4>
    [signal subscribeCompleted:^
    {
        NSLog(@"completed");//<5>
    }];
    

只要是订阅就会执行signal的block,所以不要订阅多次,因为只要重新订阅了,之前的订阅这就没有了,他会创建新的订阅者,如果我们想同时,定义next, error ,completed我们可以使用另一个方法

//注意点:不要分开订阅,要一起订阅.
    [signal subscribeNext:^(id  _Nullable x)
    {
        NSLog(@"next = %@", x);
    }
            error:^(NSError * _Nullable error)
    {
        
    }
            completed:^
    {
        NSLog(@"订阅完成");
    }];



RACSubject和RACReplaySubject

RACSubject可以用来代替代理的使用,他可以有多个订阅者, 而且多个订阅者都会被保存,


//多个订阅者
- (void)testRACSubject
{
    // 先订阅 在发送信号
    // 创建信号
    RACSubject *subject = [RACSubject subject];
    // 订阅
   // 内部创建RACSubscriber,并且保存起来
   // RACSubscriber保存nextBlock
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"第一个订阅者 %@", x);
    }];
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"第二个订阅者 %@", x);
    }];
    
    //发送信号,便利所有的订阅者
    [subject sendNext:@5];
    [subject sendNext:@3];
    
}
//先发信号,在订阅
- (void)testRACReplaySubject
{
   RACReplaySubject *replaySubject = [RACReplaySubject subject];
   // 发送信息
   // 先保存123
   // 在执行nextBlock
   [replaySubject sendNext:@"nihao"];
   [replaySubject sendNext:@3];
   // 订阅信号
      // 遍历值,让一个订阅者去发送多个值
      // 只要订阅一次,之前所有发送的值都能获取到.
   [replaySubject subscribeNext:^(id  _Nullable x)
   {
       NSLog(@"%@",x);
       
   }];
   // 多个值一个类可以拿到
   // 一个类想拿多个值,采用RACReplaySubject
   // RACReplaySubject价值 :先发送,在订阅
   // 热门 精选
   
}

RACSubject做代理使用

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

NS_ASSUME_NONNULL_BEGIN

@interface TestView : UIView

@property (nonatomic, strong)  RACSubject *subject;


@end

NS_ASSUME_NONNULL_END

#import "TestView.h"

@implementation TestView

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    NSLog(@"点击");
    [self.subject sendNext:self];
}

@end

//使用
- (void)viewDidLoad
{
    [super viewDidLoad];
    TestView *redView = [[TestView alloc] initWithFrame:CGRectMake(20, 60, 100, 100)];
    redView.backgroundColor = [UIColor redColor];
    [self.view addSubview:redView];
    
    RACSubject *oberver = [RACSubject subject];
    [oberver subscribeNext:^(id  _Nullable x)
    {
        NSLog(@"订阅%@", x);
    }];
    redView.subject = oberver;
    
    
    
}


RAC集合

在我们平时使用集合类型的时候,通常会涉及数据优化,开启一个异步线程去处理数据
RAC集合:异步线程处理数据
OC集合:数组,字典,NSSet

- (void)dictToModel
{
    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
    NSArray *dicArr = [NSArray arrayWithContentsOfFile:filePath];
     // 字典转模型
    NSMutableArray *mutArr = [NSMutableArray array];
   
    //1. 简单便利
[dicArr.rac_sequence.signal subscribeNext:^(id  _Nullable x)
  {
      NSLog(@"%@", [NSThread currentThread]);

     NSLog(@"%@", x);
   }];

    //2. 更新UI,执行完成之后操作
    [dicArr.rac_sequence.signal subscribeNext:^(id  _Nullable x)
    {
        Item *item = [Item itemWithDict:x];
        [mutArr addObject:item];
        
    } completed:^
    {   // 不管使用谁的框架,一定要记得确认线程,UI界面不显示,可能线程问题
        NSLog(@"%@",mutArr);
        NSLog(@"%@", [NSThread currentThread]);
    }];
     
}


    
    // 字典转模型
    //3.  map:映射
    // mapBlock: value参数:集合  返回值:需要映射成那个值
    //映射完成生成新的模型数组
    mutArr = [[dicArr.rac_sequence map:^id _Nullable(id  _Nullable value)
      {
        NSLog(@"%@", value);
        NSLog(@"%@", [NSThread currentThread]);
        return [Item itemWithDict:value];
    }] array];
    
    NSLog(@"%@", mutArr);
///便利字典
- (void)dictTest
{
    NSDictionary *dic =
    @{
        @"name":@"老王",
        @"age":@"18",
        @"height":@"198.0"
    };
    [dic.rac_sequence.signal subscribeNext:^(id  _Nullable x)
    {
        //RAC有个宏可以 把快速元组解析出来
        //x的第0个元素是key,1个是代表值
        RACTupleUnpack(NSString *key ,id value) = x;
        NSLog(@"key = %@ ,v = %@",key, value);
        NSLog(@"%@", x[1]);
        
    }];
}

RACMulticastConnection的使用

- (void)viewDidLoad
{
    
    [super viewDidLoad];
    // Multicast:广播连接
    // RACMulticastConnection:解决RACSignal副作用
    // 假如我们有两个地方都需要监听信号,但是信号里有个网络下载链接,因为RACSignal只有有订阅就会执行block所有,会照成下载方法多次执行的副作用,所有这里就引入了RACMulticast方法
    // 网络请求:请求一次
    @weakify(self);
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber)
    {
        @strongify(self)
        NSLog(@"执行下载");
        //以为block中使用了self,所有我们这里要进行指针的转化
        [self downloadData:^(id data) {
            [subscriber sendNext:data];
        }];
        
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"接受到数据%@", x);
    }];
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"接受到数据%@", x);
    }];
    
}

- (void)downloadData:(void (^)(id))block
{
    block(@"完成");
}

上面的例子,每当我们订阅信号,他就会执行一次下载操作,所以这是一个副作用。
我们使用RACMulticast就不可以解决这个问题,其实就是将signal转成RACMulticastConnection

    // 网络请求:请求一次
    @weakify(self);
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber)
    {
        @strongify(self)
        NSLog(@"执行下载");
        //以为block中使用了self,所有我们这里要进行指针的转化
        [self downloadData:^(id data) {
            [subscriber sendNext:data];
        }];
        
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    RACMulticastConnection *conection = [signal publish];
    
    [conection.signal subscribeNext:^(id  _Nullable x)
    {
        NSLog(@"接受到数据%@", x);
    }];
    [conection.signal subscribeNext:^(id  _Nullable x)
    {
        NSLog(@"接受到数据%@", x);
    }];
    
    // 进行连接
    /*
        RACSubject订阅signal
    */
    [conection connect];
    
}

RACCommand

RAC中处理事件的类,可以把事件的处理,事件中的数据如何传递包装到这个类中,他可以很方便的监控事件的执行过程
比如 按钮的点击,网络请求


-(void)test
{
    //处理事件
    self.view.backgroundColor = [UIColor whiteColor];
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input)
    {
        NSLog(@"执行了command的block");
        NSLog(@"%@", input);
        //必须返回一个部位空的信号
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber)
        {
            //当我们执行了command的事件之后,信号就被触发,证明他在内部被订阅了
            NSLog(@"执行了signal的block");
            //发送数据 找到谁来接受这个信号。
            //因为执行execute的时候会有一个信号的返回值,所以我们这个信号可以自己订阅
            [subscriber sendNext:@"发出信号111"];
            [subscriber sendCompleted];
            return nil;
        }];
    }];
    
//    执行事件
    [[command execute:@3] subscribeNext:^(id  _Nullable x) {
        NSLog(@"信号自己订阅%@", x);
    }];
        
    
}


    //处理事件
       self.view.backgroundColor = [UIColor whiteColor];
       RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input)
       {
           NSLog(@"执行了command的block");
           NSLog(@"%@", input);
           //必须返回一个部位空的信号
           return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber)
           {
               //当我们执行了command的事件之后,信号就被触发,证明他在内部被订阅了
               NSLog(@"执行了signal的block");
               //发送数据 找到谁来接受这个信号。
               //因为执行execute的时候会有一个信号的返回值,所以我们这个信号可以自己订阅
               [subscriber sendNext:@"发出信号111"];
               [subscriber sendCompleted];
               return [RACDisposable disposableWithBlock:^{
                   //释放资源
               }];
           }];
       }];
    //executionSignals:信号中的信号
    /*
    [command.executionSignals subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@", x);//这里传过来的值,就是 command返回的信号,所以我们可以在这里订阅信号
        [x subscribeNext:^(id  _Nullable x) {
            NSLog(@"正在执行的信号%@",x);
        }];
    }];
     */
    //另一种写法,可以直接订阅,最后一次发出的信号
    [command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x)
    {
        NSLog(@"最后一次发出的信号%@", x);
    }];
    //判断当前的信号是不是在执行,比如点击按钮下载东西
    //监听命令的执行情况
    [[command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {
        BOOL isExecuting  = [x boolValue];
        if (isExecuting )
        {
            NSLog(@"正在执行");
        }
        else
        {
            NSLog(@"执行完毕");
        }
    }];
    
    //执行事件
    [command execute:@545];

-RACCommand的常用场景
监听按钮点击,网络请求

//方式1
-(void)test
{
    self.loginBtn.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input)
       {
           NSLog(@"btnClick%@", input);
           return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
               [subscriber sendNext:@"登录"];
               [subscriber sendCompleted];
               return nil;
           }];
           
       }];
       
       //监听
       [self.loginBtn.rac_command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x)
       {
           NSLog(@"%@", x);
       }];
}


    //方式2
    RACSubject *enableSignal = [RACSubject subject];
    self.loginBtn.rac_command = [[RACCommand alloc] initWithEnabled:enableSignal signalBlock:^RACSignal * _Nonnull(id  _Nullable input)
    {
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"登录action"];
            //发送执行完毕的信号
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                [subscriber sendCompleted];
            });
            
            return nil;
        }];
    }];
    [[self.loginBtn.rac_command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {
        BOOL isLoging = [x boolValue];
        //在没有完成之前按钮不可用
        [enableSignal sendNext:@(!isLoging)];
    }];
上一篇 下一篇

猜你喜欢

热点阅读