iOS 成功执行多次以编程方式触发的 segue

iOS programmatically triggered segue is performed multiple times on success

我将我的应用程序连接到 rest API 以登录用户。登录过程完成后,我使用 NSNotificationCenter 通知控制器登录成功,并让应用程序执行 segue 以将用户带到主菜单。这是我的代码:

//perform log in, or show error message
-(void)logInAttemptComplete:(NSNotification *)notification
{
Boolean loginSuccessful = (Boolean)[[notification userInfo] objectForKey:@"loginSuccessful"];

[SVProgressHUD dismiss];
//if the suer was successfully logged in, take him to the main menu
if(loginSuccessful)
{
    //go to the main menu
    [self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{
    //copy error code and display appropriate error
    int errorCode = [[[notification userInfo] objectForKey:@"HTTP Message"] intValue];
    //404 no server, 401 wrong password/no user
    if(errorCode==401)
    {
        //create and show error alert view
        UIAlertView *loginErrorAlertView = [[UIAlertView alloc] initWithTitle:@"Log In Failure" message:@"Wrong credentials. Check your Username and/or Password." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [loginErrorAlertView show];
    }
    else
    {
        //create and show error alert view
        UIAlertView *loginErrorAlertView = [[UIAlertView alloc] initWithTitle:@"Log In Failure" message:@"Failed to contact server, please try again later." delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
        [loginErrorAlertView show];
    }
}
}

问题是,如果第一次尝试登录不成功,假设需要 X 次尝试才能成功登录,那么 segue 会执行 X 次。它当然最终出现在主菜单上,但最终结果是丑陋的。任何想法如何解决这一问题?也许我应该避免使用 segue 并以编程方式将 hte 用户直接带到另一个控制器?

编辑并回答

所以我通过添加行

犯了一个错误
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logInAttemptComplete:) name:@"logInNotification" object:nil];

在按下登录按钮的功能中。

我通过在登录尝试失败后删除观察者来修复它

if(loginSuccessful)
{
//go to the main menu
[self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"logInNotification" object:nil];

我会使用两种不同的通知,这段代码似乎比它应该的更复杂。

你没有提供你的登录码本身,所以我只能笼统地回答。创建两个通知(一个用于成功,一个用于登录失败)。登录控制器根据登录尝试的结果发送通知。

您将上述方法拆分为两个方法,这两个方法在发送任一通知时调用。

您的代码可能如下所示:

[manager GET:stringWithURLforRequest parameters:nil success:^(AFHTTPRequestOperation *task, id responseObject) {

   // ...
   [[NSNotificationCenter defaultCenter] postNotificationName:@"LogInSuccessful" object:nil userInfo:nil];

} failure:^(AFHTTPRequestOperation *task, NSError *error) {

   // ...    
   [[NSNotificationCenter defaultCenter] postNotificationName:@"LogInFailure" object:nil userInfo:@{ @"HTTP Message":statusCodeNumber}];

}];

...你只需要添加另一个通知来监听你的其他视图控制器。如果你这样做,你可以更安全地区分登录失败和成功(更好的语义)。

如果 @"loginSuccessful" 在您的 userInfo 中是 NSNumber 使用:

BOOL loginSuccessful = [notification.userInfor[@"loginSuccessful"] booleanValue];

在原始代码中,如果对象不是 nil(如果我理解正确的话,它一直是),您将始终获得值为 YES 的 loginSuccessful,因为您检查了一个对象的存在不是它的价值。

您在这一行中使用了 NSNumber:

@{@"loginSuccessful":@YES}

@YES 只是 [NSNumber numberWithBool:YES]

的缩写

根据 this header 布尔值只是 unsigned char 所以你正在签署指向这个 char 的指针,它永远是真的(不是 NULL)。

据我了解,您发送的是成功通知,如果重试,它会被调用太多次。

一种简单的解决方法是停止在成功方法中收听该通知。

所以你会有类似的东西(伪代码)

- (void)loginAttempt:(NSNotifcation*)notif{
    if (success){
//Stop listening to that notification, so the method never gets called again
    performSegueWithIndetifier
    }else{

    //retry

    }
}

另一种肮脏的单行方法是保留尝试次数的 ivar,并且仅在该次数当前等于零时才调用执行转场。

我认为我的解决方案可以解决您的问题,但如果您有时间,我建议您以一种仅在需要调用时才调用 segue 或其他 X 的回调成功的方式来审查您的设计被忽略。一种简单的想象方法是在上一次登录给出结果之前不重试。因此,在您真正知道自己是否失败之前,您不会重试(无论是超时还是成功或其他任何事情)

找到了:

问题是控制器保持 "listening" 即使由于

导致消息发送失败
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logInAttemptComplete:) name:@"logInNotification" object:nil];

在按下登录按钮的功能中。

我所要做的就是添加

[[NSNotificationCenter defaultCenter] removeObserver:self name:@"logInNotification" object:nil];

之后的行
if(loginSuccessful)
{
//go to the main menu
[self performSegueWithIdentifier:@"logInSuccessfulSegue" sender:self];
}
else
{

否则检查。