从控件中删除ReactiveCocoa信号

时间:2013-10-29 05:58:15

标签: ios objective-c reactive-cocoa

如果我将信号分配给控件的属性:

    RAC(self.loginButton.enabled) = [RACSignal
            combineLatest:@[
                    self.usernameTextField.rac_textSignal,
                    self.passwordTextField.rac_textSignal
            ] reduce:^(NSString* username, NSString* password) {
                return @(username.length > 0 && password.length > 0);
            }];

但是之后想要为RACSignal分配不同的enabled,如何清除现有的2013-10-29 16:54:50.623 myApp[3688:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Signal <RACSignal: 0x975e9e0> name: +combineLatest: ( "<RACSignal: 0x975d600> name: <UITextField: 0x10f2c420> -rac_textSignal", "<RACSignal: 0x975de30> name: <UITextField: 0x10f306e0> -rac_textSignal" ) reduce: is already bound to key path "self.loginButton.enabled" on object <LoginViewController: 0x10f264e0>, adding signal <RACSignal: 0x9763500> name: +combineLatest: ( "<RACSignal: 0x97624f0> name: <UITextField: 0x10f2c420> -rac_textSignal", "<RACSignal: 0x97629e0> name: <UITextField: 0x10f306e0> -rac_textSignal" ) reduce: is undefined behavior' 之前?

如果我第二次尝试设置它,我会得到如下例外:

{{1}}

2 个答案:

答案 0 :(得分:29)

ReactiveCocoa的很大一部分理念是消除国家。 State 是随着时间的推移可以就地改变的任何东西,并且由于以下几个原因而存在问题:

  1. 您丢失了过去的信息。一旦变量发生了变化,就像以前的价值观从未存在过一样。
  2. 更改可以来自任意数量的地方。很难查看有状态代码并确切知道将会发生什么 - locality
  3. 并发和异步使状态管理变得困难,因为您现在必须协调多个执行点之间的更改。当有多个参与者可能相互冲突时,很难实现决定论。
  4. RAC不允许多个绑定到同一属性的原因是它使排序不确定。如果我有两个绑定到enabled的信号,哪一个优先?我怎么知道哪一个发送了最新值?

    RAC不允许重新绑定同一属性的原因是它是一个有状态事情。就地更改绑定是命令式,并且由于上述所有原因而不好。

    相反,使用信号作为声明随时间变化的声明方式。改变财产的一切 - 现在或将来 - 应该用一个信号表示。

    根据您的示例,很难确切知道这些输入是什么,但是假设您想根据UISwitch的值使用不同的登录文本字段:

    // A signal that automatically updates with the latest value of
    // `self.emailLoginSwitch.on`.
    RACSignal *emailLoginEnabled = [[[self.emailLoginSwitch
        rac_signalForControlEvents:UIControlEventValueChanged]
        mapReplace:self.emailLoginSwitch]
        map:^(UISwitch *switch) {
            return @(switch.on);
        }];
    
    // Whether the user has entered a valid username and password.
    RACSignal *usernameAndPasswordValid = [RACSignal
        combineLatest:@[
            self.usernameTextField.rac_textSignal,
            self.passwordTextField.rac_textSignal
        ] reduce:^(NSString* username, NSString* password) {
            return @(username.length > 0 && password.length > 0);
        }];
    
    // Whether the user has entered a valid email address.
    RACSignal *emailValid = [self.emailTextField.rac_textSignal map:^(NSString *email) {
        return @(email.length > 0);
    }];
    
    // Uses different conditions for validity depending (ultimately) on the value of
    // `self.emailLoginSwitch`.
    RAC(self.loginButton, enabled) = [RACSignal
        if:emailLoginEnabled
        then:emailValid
        else:usernameAndPasswordValid];
    

    通过这种方式,无论输入实际是什么(用户名/密码或电子邮件),绑定仍然有效,并且我们避免了在运行时改变事物的任何需要。

答案 1 :(得分:0)

您可以明确使用:

,而不是使用RAC
[[RACSignal
    combineLatest:@[
       self.usernameTextField.rac_textSignal,
       self.passwordTextField.rac_textSignal
    ] reduce:^(NSString* username, NSString* password) {
       return @(username.length > 0 && password.length > 0);
}] setKeyPath:@"enabled" onObject:self.loginButton nilValue:nil];

对于版本&lt; 2.0,使用-toProperty:onObject:代替

我不确定它是否处理你的情况,但是尝试一下,我是ReactiveCocoa的新手,但确定有人可以帮助你,请继续关注:)