iOS:即使向其传递值,也未设置全局属性变量

时间:2015-07-08 20:22:03

标签: ios objective-c global-variables

我有一组解析XML文件以查找用户ID号的代码。此ID号将发送到另一个视图控制器以获取SOAP WSDL请求。问题是该值永远不会设置。

我的两个类是LoginPageViewController和EMIMenuTableViewController。

我正在尝试将一个整数值从LoginPageViewController传递给EMIMenuTableViewController。

这是EMIMenuTableViewController的头文件:

#import <UIKit/UIKit.h>
@interface EMIMenuTableViewController : UITableViewController
@property (nonatomic) int userNumber;
@end

这是LoginPageViewController的头文件:

#import <UIKit/UIKit.h>

@interface LoginPageViewController : UIViewController <NSXMLParserDelegate>

{
    NSXMLParser *xmlparser;

    NSString *classelement;
    NSMutableArray *titarry;
    NSMutableArray *linkarray;
    bool itemselected;
    NSMutableString *mutttitle;
    NSMutableString *mutstrlink;
    int userNumber;
    NSString *parsedString;
}

// Username and password text fields
@property (weak, nonatomic) IBOutlet UITextField *usernameTextField;
@property (weak, nonatomic) IBOutlet UITextField *passwordTextField;

// Logging in and registering
- (IBAction)loginButtonSelected:(id)sender;
@property (weak, nonatomic) IBOutlet UIButton *registerButton;

// Unwind segue IBAction for logging out
- (IBAction)logoutAndUnwind:(UIStoryboardSegue *)segue;

@end 

这是在LoginPageViewController中设置值的地方:

- (void)parserDidEndDocument:(nonnull NSXMLParser *)parser {
EMIMenuTableViewController *controller = [[EMIMenuTableViewController alloc] init];
// If the user is authenticated, allow them to proceed with the segue
if([parsedString containsString:@"User Authenticated"]) {

    >>>controller.userNumber = userNumber<<<; - set here
    NSLog(@"User:%d", userNumber);
    [self performSegueWithIdentifier:@"login_success" sender:self];
    NSLog(@"Logged In");

    // If the server says that the user isn't found, notify the user to create an account.
} else if([parsedString containsString:@"User Not Found"]) {

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"User Not Found" message:@"Please create an account!" delegate:self cancelButtonTitle:@"Close" otherButtonTitles:nil, nil];
    [alert show];

    // If the server replies that the password is incorrect, notify the user.
} else if([parsedString containsString:@"Password Incorrect"]) {

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Incorrect Username/Password" message:@"Plese try again." delegate:self cancelButtonTitle:@"close" otherButtonTitles:nil, nil];
    [alert show];

} else {

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"An error has occurred!" message:@"Please check your network connection and try again." delegate:self cancelButtonTitle:@"close" otherButtonTitles:nil, nil];
    [alert show];

}

}

以下代码显示该值未被设置(这在EMIMenuTableViewController中):

NSLog(@"User: %d", _userNumber);

此外,这是控制台输出。您可以看到我在登录的LoginPageViewController中设置了该值,但是当它记录在EMIMenuTableViewController中时,它为0。

2015-07-08 15:09:41.151 EMI[1971:173596] User:27808
2015-07-08 15:09:41.179 EMI[1971:173596] Logged In
2015-07-08 15:09:41.186 EMI[1971:173596] User: 0

我认为我这样做是正确的,但我无法弄清楚出了什么问题。我设置变量错了吗?

感谢。

3 个答案:

答案 0 :(得分:4)

EMIMenuTableViewController * controller = [[EMIMenuTableViewController alloc] init]; 将创建一个新实例,并且在您的代码中,您实际上没有使用它,这意味着您将展示EMIMenuTableViewController的其他实例。

您需要获取该呈现的EMIMenuTableViewController实例的引用,然后仅在该实例上设置userNumber

如果您使用带有segue的storyboard来呈现EMIMenuTableViewController,则该过程如下

1)给出上面提到的segue的segue标识符(这里是“segue_to_emimenutable”)

2)声明一个名为“userNumber”的全局变量,并在parserDidEndDocument方法中更新userNumber

3)然后在prepare segue方法中将其传递给EMIMenuTableViewController的呈现实例

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"segue_to_emimenutable"]) {
    EMIMenuTableViewController *destinationViewController = segue.destinationViewController;
    destinationViewController.userNumber = self.userNumber;
}

}

如果你提出任何其他方法,故事板与segue在问题中提到它,以便我的答案可以修改

答案 1 :(得分:2)

您创建并设置值的实例controller不是故事板显示时使用的实例{

您应该在prepareForSegue中执行该操作 1.将userNumber存储在LoginViewController中的全局属性中。 2.实现prepareForSegue并将值设置为MenuTableViewController

如下所示

    controller.userNumber = userNumber  //Delete this
  // Instead of the above line line,Store it in your property.. 
    self.userNumber = userNumber



- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 
{
    if ([segue.identifier isEqualToString:@"login_success"]) {
        EMIMenuTableViewController *destViewController = segue.destinationViewController;
        destViewController.userNumber = self.userNumber;
    }
}

答案 2 :(得分:2)

这一行...

EMIMenuTableViewController *controller = [[EMIMenuTableViewController alloc] init];

创建EMIMenuTable vc,然后创建此行

controller.userNumber = userNumber;

在其上设置属性。但是那个控制器只能存在几毫秒。当它超出范围时,ARC会立即释放它。

与此同时,performSegue会创建EMIMenuTableVC的新副本,而且永远不会有机会听到解析中找到的userNumber

因此我们需要从传递给segue的目标视图控制器的解析中获取该值。一种简单的方法是将userNumber声明为LoginVC的属性,例如。

@interface LoginPageViewController : UIViewController <NSXMLParserDelegate>
// ...
// more formal than your from the EMIMenuTable vc, but equivalent...
@property (nonatomic, assign) NSInteger userNumber;

这样,您的解析器不应构建自己的EMIMenuTable vc。只需在自己设置属性......

if([parsedString containsString:@"User Authenticated"]) {
    // ...
    self.userNumber = userNumber;
    // ...
    [self performSegueWithIdentifier:@"login_success" sender:self];

现在,实现为segue做准备,并在目标vc上设置值。

编辑 - 改变了这一点,理解了segue指向导航视图控制器......

// be sure to import
#import "EMIMenuTableViewController.h"
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    if ([segue.identifier isEqualToString:@"login_success"]) {
        UINavigationController *navVC = (UINavigationController *)segue.destinationViewController;
        EMIMenuTableViewController *vc = (EMIMenuTableViewController *)navVC.viewControllers[0];
        // finally
        vc.userNumber = self.userNumber;
    }
}

稍微有点迷人的方法是滥用sender:的{​​{1}}参数。然后你可以跳过LoginVC的实例变量,只需像这样直接传递值......

performSegue

这很奇怪,因为performSegue发件人是一个数字。但它会起作用,因为类型是通用的[self performSegueWithIdentifier:@"login_success" sender:@(userNumber)]; 。然后你的准备方法看起来一样,除了......

id

但我想我不建议这样做,特别是如果你刚开始的话。