为什么在ReactiveCocoa中调用两次信号?

我正在实施我的第一个代码与https://github.com/ReactiveCocoa/ReactiveCocoa 。

是为了login一个用户。 该行[subscriber sendNext:user]; 被称为两次,但我希望只有一个。 而且地图根本不会被调用(所以自动login从不会被调用)

这是我的实现:

 -(RACSignal *) login:(NSString *)email pwd:(NSString *)pwd { DDLogInfo(@"Login user %@", email); RACSignal *login = [RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber) { [PFUser logInWithUsernameInBackground:email password:pwd block:^(PFUser *user, NSError *error) { if (error) { [subscriber sendError:error]; } else { [subscriber sendNext:user]; [subscriber sendCompleted]; } }]; return nil; }]; [login map:^(PFUser *user) { return [self autoLogin:user]; }]; return login; } 

这样调用:

 NSString *email = data[@"email"]; NSString *pwd = data[@"pwd"]; [SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeBlack]; RACSignal *login = [[SyncEngine server] login:email pwd:pwd]; [login subscribeCompleted:^ { [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_LOGIN_CHANGED object:self]; [SVProgressHUD showSuccessWithStatus:LOC_OK]; [self cancelForm]; }]; [login subscribeError:^(NSError *error) { [SVProgressHUD dismiss]; [AppUrls alertError:LOC_ERROR_LOGING msg:error.userInfo[@"error"]]; }]; 

发生这种情况是因为传递给+[RACSignal createSignal:]的数据块在订购信号时执行,并且您的代码创build两个单独的订阅:

 [login subscribeCompleted:^{ ... }]; [login subscribeError:^(NSError *error) { ... }]; 

如果您只想创build单个订阅,请使用方法-[RACSignal subscribeError:completed:]

 [login subscribeError:^(NSError *error) { [SVProgressHUD dismiss]; [AppUrls alertError:LOC_ERROR_LOGING msg:error.userInfo[@"error"]]; } completed:^{ [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFY_LOGIN_CHANGED object:self]; [SVProgressHUD showSuccessWithStatus:LOC_OK]; [self cancelForm]; }]; 

虽然有时这种解决scheme可能是你所需要的,但有时候你确实想要确保订阅块只被调用一次,也许是因为它会产生副作用。 在这种情况下,您可以返callback用-replay的信号:

 return [[RACSignal createSignal:^ RACDisposable *(id<RACSubscriber> subscriber) { [PFUser logInWithUsernameInBackground:email password:pwd block:^(PFUser *user, NSError *error) { if (error) { [subscriber sendError:error]; } else { [subscriber sendNext:user]; [subscriber sendCompleted]; } }]; return nil; }] map:^(PFUser *user) { return [self autoLogin:user]; }] replay]; 

这个新的派生信号将向所有用户发送相同的消息或错误。 如果信号完成,并有一个新的用户,这将立即收到所有的消息。