0

在我的第一个视图控制器中,我有 3 个输入字段,每个字段都将用户输入输入并将其保存到一个字符串中,例如:地址、用户名和密码为NSUserDefaults. 这部分工作正常。在我的第二个视图控制器中,我试图从第一个控制器(地址、用户名和密码)中获取 3 个字符串,基于 3 个字符串创建一个 html 链接。我尝试了很多方法来访问 3 个字符串,但没有成功,我得到的结果是NULL. 这是我的代码:

//.h 文件 - 具有 3 个输入字段的第一个视图控制器CamSetup.h

 #import <UIKit/UIKit.h>
 @interface CamSetup : UIViewController <UITextFieldDelegate> 
 {
    NSString * address;
    NSString * username;
    NSString * password;

    IBOutlet UITextField * addressField;
    IBOutlet UITextField * usernameField;
    IBOutlet UITextField * passwordField; 
 }

 -(IBAction) saveAddress: (id) sender;
 -(IBAction) saveUsername: (id) sender;
 -(IBAction) savePassword: (id) sender;

 @property(nonatomic, retain) UITextField *addressField;
 @property(nonatomic, retain) UITextField *usernameField;
 @property(nonatomic, retain) UITextField *passwordField; 
 @property(nonatomic, retain) NSString *address; 
 @property(nonatomic, retain) NSString *username; 
 @property(nonatomic, retain) NSString *password;

 @end

//.m 文件 - 第一个视图控制器 CamSetup.m

#import "CamSetup.h"

@interface CamSetup ()
@end

@implementation CamSetup
@synthesize addressField, usernameField, passwordField, address, username, password;

-(IBAction) saveAddress: (id) sender
{
address = [[NSString alloc] initWithFormat:addressField.text];
[addressField setText:address];
NSUserDefaults *stringDefaultAddress = [NSUserDefaults standardUserDefaults];
[stringDefaultAddress setObject:address forKey:@"stringKey1"];
NSLog(@"String [%@]", address);
}

-(IBAction) saveUsername: (id) sender
{
  username = [[NSString alloc] initWithFormat:usernameField.text];
  [usernameField setText:username];
  NSUserDefaults *stringDefaultUsername = [NSUserDefaults standardUserDefaults];
  [stringDefaultUsername setObject:username forKey:@"stringKey2"];
  NSLog(@"String [%@]", username);
}

-(IBAction) savePassword: (id) sender
{
  password = [[NSString alloc] initWithFormat:passwordField.text];
  [passwordField setText:password];
  NSUserDefaults *stringDefaultPassword = [NSUserDefaults standardUserDefaults];
  [stringDefaultPassword setObject:password forKey:@"stringKey3"];
  NSLog(@"String [%@]", password);
}

- (void)viewDidLoad
{   
[addressField setText:[[NSUserDefaults standardUserDefaults] objectForKey:@"stringKey1"]];
[usernameField setText:[[NSUserDefaults standardUserDefaults] objectForKey:@"stringKey2"]];
[passwordField setText:[[NSUserDefaults standardUserDefaults] objectForKey:@"stringKey3"]];

[super viewDidLoad];
}

@end

//.h 第二个视图控制器 LiveView.h

 #import <UIKit/UIKit.h>
 #import "CamSetup.h"
 @interface LiveView : UIViewController
 {
  NSString *theAddress;
  NSString *theUsername;
  NSString *thePassword;
  CamSetup *camsetup; //here is an instance of the first class
}
@property (nonatomic, retain) NSString *theAddress;
@property (nonatomic, retain) NSString *theUsername;
@property (nonatomic, retain) NSString *thePassword;

@end

//.m秒查看LiveView.m文件

#import "LiveView.h"
@interface LiveView ()
@end

@implementation LiveView
@synthesize theAddress, theUsername, thePassword;

- (void)viewDidLoad
{
  [super viewDidLoad]; 
  theUsername = camsetup.username; //this is probably not right?
  NSLog(@"String [%@]", theUsername); //resut here is NULL
  NSLog(@"String [%@]", camsetup.username); //and here NULL as well
}
@end
4

5 回答 5

2

这段代码有5个问题:

  1. 如果您使用 ARC,您的 @properties 中的所有“保留”都应更改为“强”
  2. 您将 iVar 和属性命名为相同的名称。(常见的不良做法)
  3. 您总是直接访问 iVar,而不是代码中的属性。
  4. 您不会在第二个对象中保留您的 CamSetup 实例。
  5. 问题的直接原因:在第二个对象中,您只为 CamSetup 实例创建了一个占位符,您没有创建一个也没有将一个传递给它!你的第二个对象中的 self.camSetup 现在是空的。

让我们一步一步来:

首先,为您的 iVar 赋予与您的属性不同的名称。这是最佳实践,尤其是对于初学者!:)

@interface CamSetup : UIViewController <UITextFieldDelegate> 
{
    NSString *_address;
}
@property(nonatomic, strong) NSString *address;
@end

@implementation CamSetup
@synthesize address=_address;
...
@end

这很重要,因为您已经设置了属性,但在您的代码中,您并没有使用它们,而是直接访问您的 iVar。由于您已将它们命名为相同的东西,因此您可能看不到这一点。

让我们看看你的第一个对象。您代码中的每个“地址”都将转到您的 iVar 而不是财产。通常,您希望通过您的属性访问 iVar,除非您另有确定。@synthesize 为您的 iVar 创建一个 getter 和 setter 方法,该方法将保留 var,因为您在 @property 语句中告诉它。但是,当您直接访问您的 iVar 时,您不会通过这些访问器,因此您在 @properties 中编写的内容并不重要。如果你不清楚这一点,你最终可能会误解很多错误和错误。在您的第一个对象中,这仍然有效,因为 alloc/init 在对象上设置了保留,但我注意到您总是这样做,这会给您带来麻烦。

下面是使用属性时 saveAddress: 方法的样子:

-(IBAction) saveAddress: (id) sender
{
   self.address = [[NSString alloc] initWithFormat:self.addressField.text];
   [self.addressField setText:self.address];
   NSUserDefaults *stringDefaultAddress = [NSUserDefaults standardUserDefaults];
   [stringDefaultAddress setObject:self.address forKey:@"stringKey1"];
   NSLog(@"String [%@]", self.address);
}

接下来,在您的第二个对象中,您需要为 CamSetup 实例设置属性!现在,您只有一个 iVar。

 #import <UIKit/UIKit.h>
 #import "CamSetup.h"
 @interface LiveView : UIViewController
 {
  NSString *_theAddress;
  NSString *_theUsername;
  NSString *_thePassword;
  CamSetup *_camSetup; //here is an instance of the first class
}
@property (nonatomic, retain) CamSetup *camSetup; // in synthesize we'll specify that this property uses the _camSetup iVar
@property (nonatomic, strong) NSString *theAddress;
@property (nonatomic, strong) NSString *theUsername;
@property (nonatomic, strong) NSString *thePassword;

@end

实施:

#import "LiveView.h"
@interface LiveView ()
@end

@implementation LiveView
@synthesize camSetup = _camSetup;
@synthesize theAddress = _theAddress;
@synthesize theUsername = _theUsername;
@synthesize thePassword = _thePassword;

- (void)viewDidLoad
{
  [super viewDidLoad]; 
  self.theUsername = self.camSetup.username; //this is probably not right?
  // well, it's better now, but where is camSetup coming from??? it's undefined now
  NSLog(@"String [%@]", self.theUsername); //resut here is NULL
  NSLog(@"String [%@]", self.camSetup.username); //and here NULL as well
}
@end

我们创建了一个 iVar 和属性对,它将保存指向 CamSetup 实例的指针并将保留该指针(如果我们使用属性设置它)。但是,CamSetup 实例是在哪里创建的?你在哪里分配/初始化它?

这个问题有很多可能的答案。如果 CamSetup 有地址、用户名、密码的 getter,可以从您的用户默认值中读回它们,那么您所要做的就是分配/初始化一个 CamSetup 并将其设置为 camSetup。但是,现在您的第一个对象没有检索保存数据的功能,所以我们不能这样做。(不过,这是我希望您实施的解决方案)。

您可能会在您的应用程序委托中初始化这两个?但是,如果您使用情节提要,那么它可能会在初始化界面时为您初始化这些对象。在这种情况下,在您的 appDelegate 应用程序已完成启动... 方法中,您必须检索指向这些实例的指针,然后检索第二个对象上的 camSetup 属性,以指向第一个。要告诉您如何执行此操作,我们必须了解您的应用程序的详细信息。不过,这并不是最好的方法。

最佳实践是创建一个对象,为您从用户默认值中保存和检索这些数据。如果您以后想要更改存储这些数据的方式,这将证明您的实施。您只需更改它们在班级中的存储/检索方式。

于 2012-11-26T03:06:55.580 回答
1

你的问题在这里:

CamSetup *camsetup; //here is an instance of the first class 

您没有做任何事情来camsetup引用从用户那里获取输入的类的同一实例。CamSetupcamsetup变量从未初始化,因此它为 NULL,因此您尝试从中检索的任何属性也将为 NULL。

你将如何解决这个问题取决于你的程序的结构。一种可能性是让创建LiveView控制器的任何代码设置camsetup值。例如:

LiveView *liveViewController  = [LiveView alloc] initWithNibName:@"LiveView" bundle:nil]];
liveViewController.camsetup = camSetupController;

(您需要创建camsetup一个属性来执行此操作)。

但是,从设计的角度来看,让一个视图控制器引用另一个视图控制器可能是解决这个问题的错误方法——它在两个控制器之间引入了不必要的依赖关系。LiveView例如,假设您稍后决定在程序启动时使用保存的名称/密码直接进入控制器;LiveView如果依赖于从CamSetup对象获取输入,您将无法做到这一点。

因此,更好的方法可能是从和/或通过让调用的代码设置用户名/密码属性来LiveView获取其输入。NSUserDefaultsLiveView

- 编辑 -

例如,从用户默认值中检索数据,例如:

self.address = [[NSUserDefaults standardUserDefaults] objectForKey:@"stringKey1"];
于 2012-11-26T02:28:24.807 回答
0

camsetup 是否已初始化?如果不是——首先初始化它。

于 2012-11-26T00:54:41.223 回答
0

有很多方法可以做到这一点,其中大部分取决于两个控制器如何相互关联(比如你如何从第一个到第二个)。

但是,在您的情况下,您根本不需要传递字符串,因为您将它们保存在用户默认值中。只需在第二个控制器中读取这些默认值即可。

于 2012-11-26T01:43:43.397 回答
0

嗨,我有你的问题,只做一件事。当您在其中设置值时, 就NSUserDefaults需要它。synchronizationsynchronize

[stringDefaultUsername synchronize];  

并使用检索数据

NSString *username =  [[NSUserDefaults standardUserDefaults] objectForKey:@"stringKey2"];

关注苹果NSUserDefaults 类参考以获取更多信息

于 2012-11-26T11:16:06.263 回答