美文网首页
ReactiveCocoa1

ReactiveCocoa1

作者: 闹鬼的金矿 | 来源:发表于2017-07-21 16:23 被阅读4次

    RACSignal

    RAC中统一的数据接口,控件的事件,包括KVO,timer都可以转化成RACSignal。

    创建:

    1.RAC未控件的一部分原来的事件都通过Category的方式定义了event对应的signal,只需要直接拿来使用就好了。

    2.自己创建

    + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))
    didSubscribe;
    

    ​ 3.订阅

    [self.usernameTextField.rac_textSignal subscribeNext:^(id x) {
      NSLog(@"%@", x);
    }];
    

    在信号创建的时候,需要传入一个didSubscribe的block,在有其他订阅者订阅这个信号的时候,didSubscribe就会被调用,然后将数据通过subscribeNext的block传入。

    信号事件的种类

    1.next,一般情况下,信号处理业务逻辑正常返回的时候,会调用订阅者的sendNext方法将数据传入订阅者,订阅者可以通过过subscribeNext获取数据。

    2.error,有时候业务逻辑产生异常的时候,会调用订阅者的sendError方法来告知订阅者产生了异常,订阅者在subscribeNext:erro:中处理异常错误。

    3.completed,该事件表示订阅者从信号中移除,之后不再收到消息了,信号生命周期结束。

    创建一个请求账号权限的信号

    - (RACSignal *)requestAccessToTwitterSignal {
      
      // 1 - define an error
      NSError *accessError = [NSError errorWithDomain:RWTwitterInstantDomain
                                                 code:
                                                 RWTwitterInstantErrorAccessDenied
                                             userInfo:nil];
      
      // 2 - create the signal
      @weakify(self)
      return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 3 - request access to twitter
        @strongify(self)
        [self.accountStore
           requestAccessToAccountsWithType:self.twitterAccountType
             options:nil
          completion:^(BOOL granted, NSError *error) {
              // 4 - handle the response
              if (!granted) {
                [subscriber sendError:accessError];
              } else {
                [subscriber sendNext:nil];
                [subscriber sendCompleted];
              }
            }];
        return nil;
      }];
    }
    

    处理请求账号权限的信号

    [[self requestAccessToTwitterSignal]
      subscribeNext:^(id x) {
        NSLog(@"Access granted");
      } error:^(NSError *error) {
        NSLog(@"An error occurred: %@", error);
      }];
    

    信号的几种操作

    filter

    将信号过滤,只保留满足block中条件的信号

    只保留输入字符串长度大于3的信号

    [[self.usernameTextField.rac_textSignal
      filter:^BOOL(id value) {
        NSString *text = value;
        return text.length > 3;
      }]
      subscribeNext:^(id x) {
    //这里只会显示长度大于3的字符串
        NSLog(@"%@", x);
      }];
    

    这里的信号先经过filter过滤了一次,再由subscribeNext得到过滤的信号进行处理,其数据流如下图所示:

    map

    把源信号的值映射成一个新的值

    [[[self.usernameTextField.rac_textSignal
      map:^id(NSString *text) {
        return @(text.length);
      }]
      filter:^BOOL(NSNumber *length) {
        return [length integerValue] > 3;
      }]
      subscribeNext:^(id x) {
        NSLog(@"%@", x);
      }];
    

    信号先经过map映射将字符串转化成功了字符串长度的数据,再通过filter过滤掉了字符串长度等于3的信号,最后通过subscribeNext获得最后符合条件的信号,整个过程包含了不同数据类型的变化,过程如下图:

    http://7xqgnx.com1.z0.glb.clouddn.com/RAC2.png

    combineLatest

    将多个信号合并起来,每一个被合并的信号必须调用过一次sendNext,才能触发合并的信号,最后以元组的形式发出。

    reduce

    当信号发出的内容是元组时,可以使用它将元组聚合成一个值。

    将对姓名和密码的验证结果的两种信合合并成一个信号,再通过reduce返回最终是否验证通过的信号。

    RACSignal *signUpActiveSignal =
      [RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal]
                        reduce:^id(NSNumber *usernameValid, NSNumber *passwordValid
                          ) {
                          return @([usernameValid boolValue] && [passwordValid 
                            boolValue]);
                        }];
    

    数据流的状态:

    doNext

    在每次信号执行订阅者的Next方法之前,会调用这个方法,它对信号本身不会产生影响。

    在按钮点击之后,会有一系列验证的过程,在这期间,按钮不能被再次点击,等待验证结果出来之后,在恢复按钮未正常使用的状态。

     [[[[self.signInButton
       rac_signalForControlEvents:UIControlEventTouchUpInside]
       doNext:^(id x) {
         self.signInButton.enabled = NO;
         self.signInFailureText.hidden = YES;
       }]
       flattenMap:^id(id x) {
         return [self signInSignal];
       }]
       subscribeNext:^(NSNumber *signedIn) {
         self.signInButton.enabled = YES;
         BOOL success = [signedIn boolValue];
         self.signInFailureText.hidden = success;
         if (success) {
           [self performSegueWithIdentifier:@"signInSuccess" sender:self];
         }
       }];
    

    数据流的状态:

    then

    用于连接两个信号,当第一个信号完成之后(前一个信号调用sendCompleted),才会连接then返回的信号,then之前的信号会被忽略

    在得到否有权限获得账户信息之后,通过then获取搜索文本的信号,再验证搜索文本的有效性,最后获取有效的搜索文本。如果获取账号信息这一步出现错误,直接调用最后的error的block。

    [[[[self requestAccessToTwitterSignal]
      then:^RACSignal *{
        @strongify(self)
        return self.searchText.rac_textSignal;
      }]
      filter:^BOOL(NSString *text) {
        @strongify(self)
        return [self isValidSearchText:text];
      }]
      subscribeNext:^(id x) {
        NSLog(@"%@", x);
      } error:^(NSError *error) {
        NSLog(@"An error occurred: %@", error);
      }];
    

    数据流如下图:

    deliverOn

    信号传递切换到指定线程中

    现在创建一个信号在后台线程下载一个图片,可以考虑使用SDWebImage

    -(RACSignal *)signalForLoadingImage:(NSString *)imageUrl {
      
      RACScheduler *scheduler = [RACScheduler
                             schedulerWithPriority:RACSchedulerPriorityBackground];
      
      return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber
        ) {
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]
        ];
        UIImage *image = [UIImage imageWithData:data];
        [subscriber sendNext:image];
        [subscriber sendCompleted];
        return nil;
      }] subscribeOn:scheduler];
    }
    

    图片下载完成之后切换到主线程,设置图片的显示

    cell.twitterAvatarView.image = nil;
      
    [[[self signalForLoadingImage:tweet.profileImageUrl]
      deliverOn:[RACScheduler mainThreadScheduler]]
      subscribeNext:^(UIImage *image) {
       cell.twitterAvatarView.image = image;
      }];
    

    throttle

    节流,可以设置某一段时间内不发送信号,等过了这段时间,将最新的信号发出

    在用户输入搜索文字的时候,每一次textChange都会触发搜索结果的网络请求。请求次数过于频繁且没有必要,因为有时候可能是用户还没有输完而已。并且结果频繁的显示清空体验也不好。现在通过throttle处理每经过0.5秒处理一次搜索请求。

    [[[[[[[self requestAccessToTwitterSignal]
      then:^RACSignal *{
        @strongify(self)
        return self.searchText.rac_textSignal;
      }]
      filter:^BOOL(NSString *text) {
        @strongify(self)
        return [self isValidSearchText:text];
      }]
      throttle:0.5]
      flattenMap:^RACStream *(NSString *text) {
        @strongify(self)
        return [self signalForSearchWithText:text];
      }]
      deliverOn:[RACScheduler mainThreadScheduler]]
      subscribeNext:^(NSDictionary *jsonSearchResult) {
        NSArray *statuses = jsonSearchResult[@"statuses"];
        NSArray *tweets = [statuses linq_select:^id(id tweet) {
          return [RWTweet tweetWithStatus:tweet];
        }];
        [self.resultsViewController displayTweets:tweets];
      } error:^(NSError *error) {
        NSLog(@"An error occurred: %@", error);
      }];
    

    经过最后的处理,数据流如下图所示:

    在requestAccessToTwitterSignal和signalForSearchWithText都有可能产生error,最后都会直接调用subscribleNext:error方法。

    takeUnitl

    (RACSignal *) 获取信号直到某个信号执行完成

    比如,监听某个文本框的文本改变直到当前对象被销毁

    [_textField.rac_textSignal takeUntil:self.rac_willDeallocSignal];
    

    skip

    (NSUInteger) 跳过几个信号,选择忽略不处理

    第一次输入不被监听

    [[_textField.rac_textSignal skip:1] subscribeNext:^(id x) {
     
       NSLog(@"%@",x);
    }];
    

    常见的宏

    RAC(object, property)

    RAC(_titleLabel, text) = [viewModel.titleSignal takeUntil:self.
    rac_prepareForReuseSignal];
    

    表示将一个对象的属性和一个signal进行绑定,signal每产生一个value,都会自动执行如下代码

    [TARGET setValue:value ?: NIL_VALUE forKeyPath:KEYPATH]; 
    

    RACObserve(TARGET, KEYPATH)

    返回一个signal,检测target的keypath属性

    RAC和RACObserve用在一起实现双向绑定

    RAC(self.outputLabel, text) = RACObserve(self.model, name);
    

    相关文章

      网友评论

          本文标题:ReactiveCocoa1

          本文链接:https://www.haomeiwen.com/subject/adyekxtx.html