ReactiveCocoa工具书

Context

反复接触 ReactiveCocoa ,这次真的准备把它应用到实际开发中了.为了以后使用方便,这里列出一些常用关键字的使用方法,以备查询.

常用方法

简单订阅 subscribeNext

使用场景:
“ 如果你改变了,让我知道 “

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

过滤条件 filter

使用场景:

“ 如果你改变了,并且满足x条件, 那么再让我知道 “

[[self.usernameTextField.rac_textSignal

    filter:^BOOL(id value) {
      NSString* text = value;
      return text.length > 4;
    }] subscribeNext:^(id x) {
  NSLog(@"%@", x);
}];

还有拆分的写法,因为 block层级太深 ,可读性不好:

RACSignal* usernameSourceSignal = self.usernameTextField.rac_textSignal;

RACSignal* filteredUsernameSignal =
    [usernameSourceSignal filter:^BOOL(id value) {
      NSString* text = value;
      return text.length > 3;
    }];

[filteredUsernameSignal subscribeNext:^(id x) {
  NSLog(@"%@", x);
}];

类型转换 map

使用场景:

当需要从输入信号中提取不同的信息时(比如这里, 从打印下个字符,到打印长度)

[[[self.usernameTextField.rac_textSignal
    map:^id(NSString* value) {
   return @(value.length);
 }] filter:^BOOL(NSNumber* value) {
   return [value integerValue] > 4;
 }] subscribeNext:^(id x) {
   NSLog(@"%@", x);
 }];
  • 注意: 可 map 的只能是对象

RAC 宏

使用场景: RAC(A,b)
利用信号改变 A 的 b 属性值

// 验证信号
RACSignal *validUsernameSignal =
 [self.usernameTextField.rac_textSignal
 map:^id(NSString *text) {
 return @([self isValidUsername:text]);
 }]; 

RAC(self.usernameTextField, backgroundColor) =
  [validUsernameSignal
    map:^id(NSNumber *passwordValid){
     return[passwordValid boolValue] ? [UIColor clearColor]:[UIColor yellowColor];
   }];

聚合信号 combineLatest

使用场景:
多个信号条件同时满足, 才能产生有效信号(比如登陆的时候,用户名有效并且密码有效的时候,登陆按钮才应该有效)

RACSignal *signUpActiveSignal =
  [
  RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal]
                    reduce:^id(NSNumber *usernameValid, NSNumber *passwordValid){
                      return @([usernameValid boolValue]&&[passwordValid boolValue]);
                    }
  ];
  • 使用combineLatest:reduce:方法把validUsernameSignal和validPasswordSignal产生的最新的值聚合在一起,并生成一个新的信号。每次这两个源信号的任何一个产生新值时,reduce block都会执行,block的返回值会发给下一个信号。

事件信号

使用场景:
拿到UIKit控件的事件响应信号

[[self.signInButton
   rac_signalForControlEvents:UIControlEventTouchUpInside]
   subscribeNext:^(id x) {
     NSLog(@"button clicked");
   }];

封装方法

使用场景:

想把一个异步的API 封装成信号

- (RACSignal *)signInSignal {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber){
   [self.signInService 
     signInWithUsername:self.usernameTextField.text
               password:self.passwordTextField.text
               complete:^(BOOL success){
                    [subscriber sendNext:@(success)];
                    [subscriber sendCompleted];
     }];
   return nil;
}];
}

拿到信号中的信号 flattenMap

使用场景:

当需要从包含信号b的信号a中拿取信号b

[[[self.signInButton
   rac_signalForControlEvents:UIControlEventTouchUpInside]
   flattenMap:^id(id x){
     return[self signInSignal];
   }]
   subscribeNext:^(id x){
     NSLog(@"Sign in result: %@", x);
   }];

添加附加操作(Adding side-effects)

使用场景:
需要进行一些准备工作的时候

[[[[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];
     }
   }];

移除订阅 dispose

使用场景:

当需要手动释放一个信号(当没有订阅,信号就不复存在),但是使用场景很少,仅供了解

RACSignal *backgroundColorSignal =
    [self.searchText.rac_textSignal 
        map:^id(NSString *text) { 
            return [self isValidSearchText:text] ? 
                [UIColor whiteColor] : [UIColor yellowColor]; 
    }]; 
    
RACDisposable *subscription = 
    [backgroundColorSignal 
        subscribeNext:^(UIColor *color) {
            self.searchText.backgroundColor = color; 
    }]; 
    
// at some point in the future ... 
[subscription dispose];

防止循环引用

@weakify(self) 
[[self.searchText.rac_textSignal 
    map:^id(NSString *text) { 
        return [self isValidSearchText:text] ? 
            [UIColor whiteColor] : [UIColor yellowColor]; 
    }] 
    subscribeNext:^(UIColor *color) { 
        @strongify(self) 
        self.searchText.backgroundColor = color; 
    }];

next error completed

在signal的生命周期中,它可能不发送事件,发送一个或多个next事
件,在这之后还能发送一个completed事件或一个error事件。

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

信号链接 then

使用场景:
当后面的信号需要依赖前面的信号时

[[[self requestAccessToTwitterSignal] 
    then:^RACSignal *{ 
        @strongify(self) 
        return self.searchText.rac_textSignal; 
    }] 
    subscribeNext:^(id x) { 
        NSLog(@"%@", x); 
    } error:^(NSError *error) { 
        NSLog(@"An error occurred: %@", error); 
    }];
  • then方法会等待completed事件的发送,然后再订阅由then block返回的signal。这样就高效地把控制权从一个signal传递给下一个。

  • then方法会跳过error事件,因此最终的subscribeNext:error: block还是会收到获取访问权限那一步发送的error事件。

异步信号

使用场景:
后台加载资源

-(RACSignal *)signalForLoadingImage:(NSString *)imageUrl { 

    RACScheduler *scheduler = [RACScheduler 
        schedulerWithPriority:RACSchedulerPriorityBackground]; 
        
    return [[RACSignal createSignal:^RACDisposable *(id subscriber) { 
    
        NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]]; 
        UIImage *image = [UIImage imageWithData:data]; 
    
    
        [subscriber sendNext:image]; 
        [subscriber sendCompleted]; 
        return nil; 
    }] subscribeOn:scheduler]; 
}
  • subscribeOn:来确保signal在指定的scheduler上执行。

在主线程上更新UI

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

针对cell的重用问题, 有种更优化的方法:

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

延时响应 throttle

使用场景:

当用户输入完毕, 自定进行搜索的时候,不应该用户每次改变输入,都马上搜索,应该当用户停止输入 x 秒之后,再进行搜索

@weakify(self);
[[self.passwordTextField.rac_textSignal throttle:2] subscribeNext:^(id x) {
  @strongify(self);
  self.hintLabel.text = (NSString*)x;
}];

参考网址:
http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1
http://benbeng.leanote.com/post/ReactiveCocoaTutorial-part1

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容