iOS: 全局 属性 变量即使在传递值时也未设置
iOS: Global property variable not being set even when value is passed to it
我有一组代码可以解析 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
我认为我这样做是对的,但我不知道哪里出了问题。我设置变量错误吗?
谢谢。
您创建并设置值的实例 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;
}
}
这一行...
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。只需将 属性 设置为 self...
if([parsedString containsString:@"User Authenticated"]) {
// ...
self.userNumber = userNumber;
// ...
[self performSegueWithIdentifier:@"login_success" sender:self];
现在,实施 segue 准备,并在目标上设置值 vc。
EDIT - 改变了这一点,理解为 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;
}
}
一个稍微有趣的方法是滥用 performSegue
的 sender:
参数。然后就可以跳过LoginVC的实例变量了,直接传值就可以了...
[self performSegueWithIdentifier:@"login_success" sender:@(userNumber)];
这很奇怪,因为 performSegue 发件人是一个数字。但它会起作用,因为类型是通用的 id
。你的 prepare 方法看起来是一样的,除了...
if ([segue.identifier isEqualToString:@"login_success"]) {
UINavigationController *navVC = (UINavigationController *)segue.destinationViewController;
EMIMenuTableViewController *vc = (EMIMenuTableViewController *)navVC.viewControllers[0];
NSNumber *paramTrick = (NSNumber *)sender;
vc.userNumber = [paramTrick intValue];
}
但我想我不推荐这种方式,特别是如果你刚开始。
EMIMenuTableViewController *controller = [[EMIMenuTableViewController alloc] init];
将创建一个新实例,而在您的代码中您实际上并没有使用它,这意味着您呈现了 EMIMenuTableViewController 的其他一些实例。
您需要获取所呈现的 EMIMenuTableViewController 实例的引用,然后仅在该实例上设置 userNumber
如果您使用带有 segue 的故事板来呈现 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 的故事板的任何其他方法,请在问题中提及它,以便我的答案可以针对该方法进行修改
我有一组代码可以解析 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
我认为我这样做是对的,但我不知道哪里出了问题。我设置变量错误吗?
谢谢。
您创建并设置值的实例 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;
}
}
这一行...
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。只需将 属性 设置为 self...
if([parsedString containsString:@"User Authenticated"]) {
// ...
self.userNumber = userNumber;
// ...
[self performSegueWithIdentifier:@"login_success" sender:self];
现在,实施 segue 准备,并在目标上设置值 vc。
EDIT - 改变了这一点,理解为 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;
}
}
一个稍微有趣的方法是滥用 performSegue
的 sender:
参数。然后就可以跳过LoginVC的实例变量了,直接传值就可以了...
[self performSegueWithIdentifier:@"login_success" sender:@(userNumber)];
这很奇怪,因为 performSegue 发件人是一个数字。但它会起作用,因为类型是通用的 id
。你的 prepare 方法看起来是一样的,除了...
if ([segue.identifier isEqualToString:@"login_success"]) {
UINavigationController *navVC = (UINavigationController *)segue.destinationViewController;
EMIMenuTableViewController *vc = (EMIMenuTableViewController *)navVC.viewControllers[0];
NSNumber *paramTrick = (NSNumber *)sender;
vc.userNumber = [paramTrick intValue];
}
但我想我不推荐这种方式,特别是如果你刚开始。
EMIMenuTableViewController *controller = [[EMIMenuTableViewController alloc] init]; 将创建一个新实例,而在您的代码中您实际上并没有使用它,这意味着您呈现了 EMIMenuTableViewController 的其他一些实例。
您需要获取所呈现的 EMIMenuTableViewController 实例的引用,然后仅在该实例上设置 userNumber
如果您使用带有 segue 的故事板来呈现 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 的故事板的任何其他方法,请在问题中提及它,以便我的答案可以针对该方法进行修改