从控件中删除一个ReactiveCocoa信号

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

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

但是之后又想分配一个不同的RACSignalenabled ,我怎么才能清除现有的RACSignal呢?

如果我尝试再次设置,则会出现如下exception:

 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' 

ReactiveCocoa哲学的一大部分是消除国家。 状态是随着时间的推移可以改变的任何事情,这是有问题的,原因如下:

  1. 你失去了过去的信息。 一旦一个variables被改变,就像之前的值不存在一样。
  2. 变化可能来自任何地方。 很难看到有状态的代码,并确切地知道什么时候会发生什么 – 当地贫困。
  3. 并发性和asynchronous性使状态pipe理变得困难 ,因为您现在必须协调跨多个执行点的更改。 当存在多个可能相互冲突的行动者时,决定论很难实现。

RAC不允许多个绑定到同一个属性的原因是它使得sorting不确定。 如果我有两个信号绑定enabled ,哪一个优先? 我怎么知道哪一个发送了最新值?

RAC不允许重印相同的财产的原因是这是一个有状态的事情。 改变现场的绑定势在必行 ,而且不利于上述所有原因。

相反,使用信号作为声明方式来expression随着时间的变化 。 所有改变财产的东西 – 现在或将来 – 都应该用一个信号来表示。

根据你的例子,很难确切地知道这些input是什么,但是假设你想根据UISwitch的值使用不同的login文本字段:

 // 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]; 

通过这种方式,无论input实际是什么(用户名/密码或电子邮件),绑定都是有效的,而且我们避免了在运行时发生变异的任何需要。

而不是使用RACmacros,你可以明确地使用:

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

对于版本<2.0,请使用-toProperty:onObject:来代替

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