您的位置:首页 > Web前端 > React

ReactiveCocoa基础和一些常见类介绍

2016-05-16 17:02 666 查看
一 导入ReactiveCocoa框架

可以使用CocoaPods(用于管理第三方框架的插件)帮助我们导入

podfile如果只描述pod 'ReactiveCocoa', '~> 4.0.2-alpha-1',会导入不成功 

因为用到了swift的一些东西
需要在podfile加上use_frameworks,重新pod install 才能导入成功

ReactiveCocoa常见的一些类:

RACSignal:信号类,一般表示将来有数据传递,只要有数据改变,信号内部接收到数据,就会马上发出数据。

        1.信号类(RACSiganl),只是表示当数据改变时,信号内部会发出数据,它本身不具备发送信号的能力,而是交给内部一个订阅者去发出

         2.默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。

 3.如何订阅信号:调用信号RACSignal的subscribeNext就能订阅

简单使用:

//  1.创建信号  RACSubscriber是订阅者

      RACSignal *racsignal =  [RACSignal createSignal:^RACDisposable *(id<RACSubscriber>
subscriber) {

      // 3.发送信号

      [subscriber sendNext:@100];

      return nil;

  }];

    

       //  2.订阅信号

      [racsignal subscribeNext:^(id x) {

         NSLog(@"%@",x);

     }];

RACSubscriber:表示订阅者的意思用于发送一个信号,这是一个协议,不是一个类,只要遵守这个协议并且实现方法才能成为订阅者,通过create创建信号,都有一个订阅者,帮助他发送数据

RACDisposable:用于取消订阅者或者清理资源,当信号发送完成或者发送错误的时候就会自动触发取消订阅

   使用场景:不想监听某个信号时候可以用他主动取消订阅信号

   例如:

  //  1.创建信号

  RACSignal *racsignal =  [RACSignal createSignal:^RACDisposable *(id<RACSubscriber>
subscriber) {

      // 3.发送信号

      [subscriber sendNext:@100];

      

      // 订阅者subscriber不会被销毁永远不会取消订阅(在不主动取消的情况下)

      _subscriber = subscriber;// _subscriber成员变量强引用一次

      self.disposable = [RACDisposable disposableWithBlock:^{

          NSLog(@"信号被取消订阅");

      }];

      return self.disposable;

  }];

    //  2.订阅信号 subscribeNext在函数内部创建订阅者

     [racsignal subscribeNext:^(id x) {

         NSLog(@"%@",x);

     }];

    

    // 取消订阅

    [self.disposable dispose];

RACSubject:信号提供者,自己可以充当信号又可以发送信号

使用场景,通常用来代替代理,有了它就可以不用定义代理了。

RACReplaySubject:重复提供信号类 RACSubject的子类

RACReplaySubject和RACSubject的区别:

RACReplaySubject可以先发送信号在订阅信号,RACSubject不可以。

RACSubject使用步骤:

// 1.创建信号 [RACSubject subject],跟RACSiganl不一样,创建信号时没有block

    RACSubject *subject = [RACSubject subject];

    

   

    // 2.订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock

    // 返回的是订阅者

   RACDisposable *posable1 =  [subject subscribeNext:^(id x) {

        NSLog(@"第一个订阅者%@",x);

    }];

   RACDisposable *posable2 = [subject subscribeNext:^(id x) {

        NSLog(@"第二个订阅者%@",x);

    }];

    

    // RACSubject:底层实现和RACSignal不一样。

    // 1.调用subscribeNext订阅信号,只是把订阅者保存起来,并且订阅者的nextBlock已经赋值了。

    // 2.调用sendNext发送信号,遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock。

    

    //3.发送信号 sendNext:(id)value

    [subject sendNext:@100];

    [posable1 dispose];// 取消订阅信号

    [subject sendNext:@100];

RACReplaySubject:使用步骤

 RACReplaySubject *subjest = [RACReplaySubject subject];

    

    //2.调用subscribeNext订阅信号,遍历保存的所有值,一个一个调用订阅者的nextBlock

    RACDisposable *posable1 = [subjest subscribeNext:^(id x) {

        NSLog(@"第一个订阅者%@",x);

    }];

    

    RACDisposable *posable2 = [subjest subscribeNext:^(id x) {

        NSLog(@"第二个订阅者%@",x);

    }];

    //调用sendNext发送信号,把值保存起来,然后遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock

    [subjest sendNext:@100];

    [posable1 dispose];// 取消订阅

    

    [subjest sendNext:@120];

RAC中的 集合类

RACTuple:元组类,类似NSArray,用来包装值

RACSequence:Rac中的集合类,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典

RACSequence使用场景:字典转模型

RACTuple简单使用:

// 将数组转换层一个元组 RACTuple

    RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:@[@"1",@"2",@3,@4,@5]];

    // 将元组转换为rac中的集合类RACSequence

    RACSequence *sequence = tuple.rac_sequence;

    // 在将集合类(RACSequence)转换为信号(RACSignal)

    RACSignal *signal =  sequence.signal;

    // 订阅信号,激活信号,会自动把集合中的所有值,遍历出来

    [signal subscribeNext:^(id x) {

         NSLog(@"%@",x);

    }];

    

    //可以链式调用

    [tuple.rac_sequence.signal subscribeNext:^(id x) {

        NSLog(@"%@",x);

    }];

   

遍历字典:

     NSDictionary *dict = @{@"name":@"程倩",@"age":@"26",@"sex":@"男"};

    // 遍历字典

    [dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {

        

        NSString *key = x[0];

        NSString *value = x[1];

        NSLog(@"%@  %@",key,value);

        

    }];

  第二种遍历方式:

   NSDictionary *dict = @{@"name":@"程倩",@"age":@"26",@"sex":@"男"};

    // 遍历字典

    [dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {

        

        // RACTupleUnpack  相当于 NSString *key = x[0];

        //                       NSString *value = x[1];

        RACTupleUnpack(NSString *key,NSString *value) = x;

        NSLog(@"%@  %@",key,value);

        

    }];

字典转模型:

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];

    NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];

    NSMutableArray *flags = [NSMutableArray array];

    _flags = flags;

    

    // rac_sequence注意点:调用subscribeNext,并不会马上执行nextBlock,而是会等一会。

    [dictArr.rac_sequence.signal subscribeNext:^(id x) {

        // 运用RAC遍历字典,x:字典

        FlagItem *item = [FlagItem flagWithDict:x];

        [flags addObject:item];

    }];

    

    // 3.3 RAC高级写法:

    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];

    

    NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];

    // map:映射的意思,目的:把原始值value映射成一个新值

    // array: 把集合转换成数组

    // 底层实现:当信号被订阅,会遍历集合中的原始值,映射成新值,并且保存到新的数组里。

    NSArray *flags = [[dictArr.rac_sequence map:^id(id value) {

        return [FlagItem flagWithDict:value];

    }] array];

ReactiveCocoa开发中常见用法:

1.代替代理

  
rac_signalForSelector
:用于替代代理

   //rac_signalForSelector:将某个select转换为一个信号,然后订阅这个信号,系统调用这个函数的时候会发出信号

    // 可以用来代替代理(但是不能传值)如果想传值使用RACSubject

    [[self rac_signalForSelector:@selector(didReceiveMemoryWarning)] subscribeNext:^(id x)
{

        NSLog(@"系统调用了didReceiveMemoryWarning函数");

    }];

 

  2. 代替KVO:

   
rac_valuesAndChangesForKeyPath
:用于监听某个对象的属性改变

  // 代替KVO监听属性的改变这个函数需要导入NSObject+RACKVOWrapper.h头文件,这个文件默认没有导入

    [self rac_observeKeyPath:@"name" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent)
{

        

        NSLog(@"rac_observeKeyPath监听到值改变了");

        

    }];

    //也可以将值改变转换为一个信号 然后订阅这个信号

    [[self rac_valuesForKeyPath:@"name" observer:nil] subscribeNext:^(id x)
{

        NSLog(@"rac_valuesForKeyPath监听到值改变了");

    }];

  3.监听事件:

    
rac_signalForControlEvents
:用于监听某个事件

    //监听按钮点击事件,返回一个信号,然后直接订阅这个信号

    [[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x)
{

        NSLog(@"按钮被点击了");

    }];

 4.代替通知

  
rac_addObserverForName
:用于监听某个通知

 // 代替通知中心

    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"通知名称" object:nil] subscribeNext:^(id x)
{

        NSLog(@"收到通知");

    }];

    [[NSNotificationCenter defaultCenter] postNotificationName:@"通知名称" object:nil];

5. 监听文本框文字改变

rac_textSignal
:只要文本框发出改变就会发出这个信号

 // 监听文本框值的改变

    [[self.textfield rac_textSignal] subscribeNext:^(id x)
{

        NSLog(@"%@",x);//x是文本框中的值

    }];

6. 处理当界面有多次请求时,需要都获取到数据时,才能展示界面(两个信号都发送的
时候才会调用最后的select)

    // 6.处理多个请求,都返回结果的时候,统一做处理.

    RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber>
subscriber) {

        // 发送请求1

        [subscriber sendNext:@"发送请求1"];

        return nil;

    }];

    

    RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber>
subscriber) {

        // 发送请求2

        [subscriber sendNext:@"发送请求2"];

        return nil;

    }];

    // 使用注意:几个信号,参数一的方法就几个参数,每个参数对应信号发出的数据。

    [self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[request1,request2]];

ReactiveCocoa常见宏

1.RAC(TARGET, [KEYPATH, [NIL_VALUE]])
:用于给某个对象的某个属性绑定。

 // 只要文本框中的文字改变就会修改lable中的文字

    RAC(self.label,text) = self.textfield.rac_textSignal;

2.
RACObserve(self, name)
:监听某个对象的某个属性,返回的是信号。
  //RACObserve 监听某个对象的某个值,返回的是一个信号,其实就是ac_valuesForKeyPath函数

    [RACObserve(self, name) subscribeNext:^(id x) {

        

        NSLog(@"%@",x);

    }];

3.  
@weakify(Obj)和@strongify(Obj)
,一般两个都是配套使用,解决循环引用问题.

 @weakify(self);// 将self转换成弱指针引用 

    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

       

        @strongify(self) // 将self转换为强指针引用,只在当前block中有效

        

        NSLog(@"%@",self);

        

        return nil;

    }];

    _signal = signal;

 

4.
RACTuplePack
:把数据包装成RACTuple(元组类)

// 把参数中的数据包装成元组

    RACTuple *tuple = RACTuplePack(@10,@20);

5.
RACTupleUnpack
:把RACTuple(元组类)解包成对应的数据。

// 把参数中的数据包装成元组

    RACTuple *tuple = RACTuplePack(@"xmg",@20);

    // 解包元组,会把元组的值,按顺序给参数里面的变量赋值

    // name = @"xmg" age = @20

    RACTupleUnpack(NSString *name,NSNumber *age) = tuple;

RACMulticastConnection:当一个信号被多次订阅的时候,为了保证创建信号时避免多次调用创建信号中的block造成副作用可以使用RACMulticastConnection

 RACMulticastConnection通过RACSignal的-publish或者-muticast:方法创建

简单使用:

  // RACMulticastConnection使用步骤:

    // 1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe

    // 2.创建连接 RACMulticastConnection *connect = [signal publish];

    // 3.订阅信号,注意:订阅的不在是之前的信号,而是连接的信号。 [connect.signal subscribeNext:nextBlock]

    // 4.连接 [connect connect]

    

    // RACMulticastConnection底层原理:

    // 1.创建connect,connect.sourceSignal -> RACSignal(原始信号)  connect.signal
-> RACSubject

    // 2.订阅connect.signal,会调用RACSubject的subscribeNext,创建订阅者,而且把订阅者保存起来,不会执行block。

    // 3.[connect connect]内部会订阅RACSignal(原始信号),并且订阅者是RACSubject

    // 3.1.订阅原始信号,就会调用原始信号中的didSubscribe

    // 3.2 didSubscribe,拿到订阅者调用sendNext,其实是调用RACSubject的sendNext

    // 4.RACSubject的sendNext,会遍历RACSubject所有订阅者发送信号。

    // 4.1 因为刚刚第二步,都是在订阅RACSubject,因此会拿到第二步所有的订阅者,调用他们的nextBlock

    

    

    // 需求:假设在一个信号中发送请求,每次订阅一次都会发送请求,这样就会导致多次请求。

    // 解决:使用RACMulticastConnection就能解决.

    

    // RACMulticastConnection:解决重复请求问题

    // 1.创建信号

    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber>
subscriber) {

        NSLog(@"发送请求");

        [subscriber sendNext:@1];

        return nil;

    }];

    

    // 2.创建连接

    RACMulticastConnection *connect = [signal publish];

    

    // 3.订阅信号,

    // 注意:订阅信号,也不能激活信号,只是保存订阅者到数组,必须通过连接,当调用连接,就会一次性调用所有订阅者的sendNext:

    [connect.signal subscribeNext:^(id x) {

        NSLog(@"订阅者一信号");

    }];

    [connect.signal subscribeNext:^(id x) {

        NSLog(@"订阅者二信号");

    }];

    // 4.连接,激活信号

    [connect connect];

RACCommand:Rac中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递包装到这个类中,可以很方便的监听事件的执行过程。

 

使用场景:监听按钮点击和网络请求。

  // RACCommand:处理事件

    // RACCommand:不能返回一个空的信号,会闪退

    // 创建命令

   RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(id input)
{

       // 当前block执行命令的时候调用

       // input 执行命令传入参数

       NSLog(@"%@",input);

       return  [RACSignal createSignal:^RACDisposable *(id<RACSubscriber>
subscriber) {

           [subscriber sendNext:@"执行命令产生数据"];

           return nil;

       }];

   }];

    // 如果要拿到执行命令产生的数据,执行命令返回一个信号,直接订阅信号

   RACSignal *signal =  [command execute:@1];

    // 订阅信号

    [signal subscribeNext:^(id x) {

        NSLog(@"%@",x);

    }];

executionSignals:信号中的信号,发送的数据就是信号

 //executionSignals:信号中的信号,发送的数据就是信号

    // 订阅信号,必须要在执行命令之前订阅

    [command.executionSignals subscribeNext:^(RACSignal *x) {

        [x subscribeNext:^(id x) {

             NSLog(@"%@",x);

        }];

    }];

    [command execute:@100];

switchToLatest:获取最新发送的信号,只能用于信号中信号

// switchToLatest获取最新发送的信号,只能用于信号中信号

    // 获取到最新发送的信号直接订阅

    [command.executionSignals.switchToLatest subscribeNext:^(id x)
{

         NSLog(@"%@",x);

    }];

    [command execute:@100];

监听任务是否执行完毕:

   RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(id input)
{

      NSLog(@"执行命令传入参数%@",input);

      

      return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber>
subscriber) {

         

          [subscriber sendNext:@"执行命令产生数据"];

          // 发送完成

          [subscriber sendCompleted];

          return nil;

      }];

  }];

    

    // 监听任务是否执行完成

    [command.executing subscribeNext:^(id x) {

        if([x boolValue]==YES)

        {

            NSLog(@"当前任务正在执行");

        }else{

            NSLog(@"当前任务执行没有在执行");

        }

    }];

    

    [command execute:@0];

不正确之处,欢迎补充
测试代码 https://github.com/CharType/ReactiveCocoaTest
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ReactiveCocoa