Почему сигнал вызывается дважды в ReactiveCocoa?
Я реализую свой первый код с помощью https://github.com/ReactiveCocoa/ReactiveCocoa.
Это для входа в систему пользователя. Линия [subscriber sendNext:user];
вызывается дважды, но я ожидаю, что будет только один. А карта вообще не вызывается (поэтому никогда не вызывается автологин)
Это моя реализация:
-(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"]];
}];
2 ответа
Это происходит потому, что блок передан +[RACSignal createSignal:]
выполняется всякий раз, когда создается подписка на сигнал, а ваш код создает две отдельные подписки:
[login subscribeCompleted:^{ ... }];
[login subscribeError:^(NSError *error) { ... }];
Если вы хотите создать только одну подписку, используйте метод -[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];
}];
Хотя иногда это решение может быть всем, что вам нужно, иногда вам нужно убедиться, что блок подписки вызывается только один раз, возможно, из-за побочных эффектов. В этом случае вы можете вернуть сигнал вызова -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];
Этот новый, производный сигнал будет отправлять одинаковые сообщения или ошибки всем подписчикам. Если сигнал завершен, и есть новый подписчик, он сразу же получит все сообщения.