【问题标题】:Why the signal is called twice in ReactiveCocoa?为什么信号在 ReactiveCocoa 中被调用两次?
【发布时间】:2013-11-28 22:23:44
【问题描述】:

我正在使用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"]];
}];

【问题讨论】:

  • 单步执行代码,看看发生了什么。或者在 sendNext 的入口处设置断点,并在每次调用时检查调用堆栈。

标签: ios objective-c reactive-programming reactive-cocoa


【解决方案1】:

发生这种情况是因为每当订阅信号时传递给+[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];
    }];

【讨论】:

    【解决方案2】:

    虽然有时this solution 可能就是您所需要的,但有时您确实希望确保只调用一次订阅块,可能是因为它会产生副作用。在这种情况下,您可以返回调用-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];
    

    这个新的派生信号将向所有订阅者发送相同的消息或错误。如果信号完成,并且有新的订阅者,它也会立即收到所有消息。

    【讨论】:

      猜你喜欢
      • 2021-07-13
      • 2021-11-25
      • 2015-12-24
      • 2012-12-10
      • 2014-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多