简体   繁体   English

无法在视图之间读取NSUserDefaults数据

[英]Can't read NSUserDefaults data between views

Disclaimer: major noob 免责声明:主要菜鸟

I'm writing an arithmetic flash card app as a learning project. 我正在编写一个算术闪存卡应用程序作为学习项目。 I have a UITabViewController with the bottom tab bar that toggles between a few different views. 我有一个UITabViewController,底部的标签栏可以在几个不同的视图之间切换。 Everything works okay until I try to set NSUserDefault boolean values in the Settings view controller and try to read those values in the Flashcards view controller. 一切正常,直到我尝试在Settings视图控制器中设置NSUserDefault布尔值并尝试在Flashcards视图控制器中读取这些值。

The settings view has a switch to enable/disable each operator (addition, subtraction, etc) and the flashcard view should randomly present a flash card if that type of operation was enabled. 设置视图有一个开关,用于启用/禁用每个操作符(加法,减法等),如果启用了此类操作,则闪卡视图应随机显示闪存卡。

I believe that my mistake is that I don't understand the key concept of data encapsulation, objects, etc. I'd appreciate any help. 我相信我的错误是我不理解数据封装,对象等的关键概念。我很感激任何帮助。

Here is the Settings view controller. 这是“设置”视图控制器。 I'm not even sure how to put the code into this forum so this might be a laughable moment... here goes: 我甚至不确定如何将代码放入此论坛,所以这可能是一个可笑的时刻......这里是:

//  settings_flashcards.h

#import <UIKit/UIKit.h>
@interface settings_flashcards : UIViewController {
UISwitch *additionSwitch;
UISwitch *subtractionSwitch;
UISwitch *multiplicationSwitch;
UISwitch *divisionSwitch;
}

@property (nonatomic,retain) IBOutlet UISwitch *additionSwitch;
@property (nonatomic,retain) IBOutlet UISwitch *subtractionSwitch;
@property (nonatomic,retain) IBOutlet UISwitch *multiplicationSwitch;
@property (nonatomic,retain) IBOutlet UISwitch *divisionSwitch;

@end

and... 和...

/  settings_flashcards.m

#import "settings_flashcards.h"

@implementation settings_flashcards

@synthesize additionSwitch;
@synthesize subtractionSwitch;
@synthesize multiplicationSwitch;
@synthesize divisionSwitch;

- (void)viewDidLoad {

[additionSwitch addTarget:self action:@selector(additionSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[subtractionSwitch addTarget:self action:@selector(subtractionSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[multiplicationSwitch addTarget:self action:@selector(multiplicationSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[divisionSwitch addTarget:self action:@selector(divisionSwitchFlipped) forControlEvents:UIControlEventValueChanged];
[super viewDidLoad];
}

-(void) additionSwitchFlipped {
if (additionSwitch.on) {
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"additionKey"];
}else {
    [[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:@"additionKey"];
}
}

-(void) subtractionSwitchFlipped {
if (subtractionSwitch.on) {
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"subtractionKey"];  
}else {
    [[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:@"subtractionKey"]; 
}
}

-(void) multiplicationSwitchFlipped {
if (multiplicationSwitch.on) {
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"multiplicationKey"];   
}else {
    [[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:@"multiplicationKey"];  
}

}

-(void) divisionSwitchFlipped {
if (divisionSwitch.on) {
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"divisionKey"]; 
}else {
    [[NSUserDefaults standardUserDefaults] setBool:FALSE forKey:@"divisionKey"];    
}
}

Here is the Flashcards view... 这是Flashcards视图......

//  flashcardsViewController.h

#import <UIKit/UIKit.h>

@interface flashcardsViewController : UIViewController <UIActionSheetDelegate>{

UILabel *firstNumberLabel;
UILabel *secondNumberLabel;
UILabel *answerNumberLabel;
UILabel *operatorLabel;

BOOL additionIsEnabled;
BOOL subtractionIsEnabled;
BOOL multiplicationIsEnabled;
BOOL divisionIsEnabled;
}

@property (nonatomic,retain) IBOutlet UILabel *firstNumberLabel;
@property (nonatomic,retain) IBOutlet UILabel *secondNumberLabel;
@property (nonatomic,retain) IBOutlet UILabel *answerNumberLabel;
@property (nonatomic,retain) IBOutlet UILabel *operatorLabel;

-(void) buttonClicked:(id)sender;

@end

and... 和...

//  flashcardsViewController.m

#import "flashcardsViewController.h"

@implementation flashcardsViewController

@synthesize firstNumberLabel;
@synthesize secondNumberLabel;
@synthesize answerNumberLabel;
@synthesize operatorLabel;

- (void)viewDidLoad {

srand(time(0)); //seed random

//the following should assign the keys if they don't exist  
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"additionKey"]){
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"additionKey"];
}   


if (![[NSUserDefaults standardUserDefaults] boolForKey:@"subtractionKey"]) {
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"subtractionKey"];
}

if (![[NSUserDefaults standardUserDefaults] boolForKey:@"multiplicationKey"]) {
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"multiplicationKey"];

}

if (![[NSUserDefaults standardUserDefaults] boolForKey:@"divisionKey"]) {
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"divisionKey"];
}

//the following should assign each BOOL variable based on the key
additionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"additionKey"];
subtractionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"subtractionKey"];
multiplicationIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"multiplicationKey"];
divisionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey:@"divisionKey"];
[super viewDidLoad];
}

-(void) buttonClicked:(id)sender{

int a = rand() %  4;// random number generator (number to enter loop)   

if ( additionIsEnabled || subtractionIsEnabled || multiplicationIsEnabled || divisionIsEnabled) {
    while (a < 5) {
        switch (a) {
            case 0:
                if (additionIsEnabled == TRUE){
                    int x = rand() % 11 + 1;
                    int y = rand() %11 + 1;
                    firstNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",x];
                    secondNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",y];
                    answerNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",(x+y)];
                    operatorLabel.text = [[NSString alloc]initWithFormat: @"+"];
                    a = 5;
                }
                else a++;
                break;
            case 1:
                if (subtractionIsEnabled == TRUE){
                    int x = rand() % 19 + 1;
                    int y = rand() %11 + 1;
                    firstNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",x];
                    secondNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",y];
                    answerNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",(x-y) ];
                    operatorLabel.text = [[NSString alloc]initWithFormat: @"-"];
                    a = 5;
                }
                else a++;
                break;
            case 2:
                if (multiplicationIsEnabled == TRUE){
                    int x = rand() % 11 + 1;
                    int y = rand() %11 + 1;
                    firstNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",x];
                    secondNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",y];
                    answerNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",(x*y)];
                    operatorLabel.text = [[NSString alloc]initWithFormat: @"×"];
                    a = 5;
                }
                else a++;
                break;
            case 3:
                if (divisionIsEnabled == TRUE){
                    int x = rand() % 11 + 1;
                    int y = rand() % 11 + 1;
                    firstNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",(x*y)];
                    secondNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",y];
                    answerNumberLabel.text = [[NSString alloc]initWithFormat: @"%i",x];
                    operatorLabel.text = [[NSString alloc]initWithFormat: @"÷"];
                    a = 5;
                }
                else a = 0;
                break;
                default:
                break;
        }
    }       
}
else
{
    UIAlertView *noOperatorSelectedAlert = [[UIAlertView alloc]initWithTitle:@"You have not set any operations."
                                                   message:@"Return to the settings menu and decide which operations you wish to perform. (addition, subtraction, etc.)"
                                                   delegate:self
                                                   cancelButtonTitle:@"Ok" 
                                                   otherButtonTitles:nil];
    [noOperatorSelectedAlert show];
    [noOperatorSelectedAlert release];
}
}

There are a few things to do here. 这里有一些事情要做。 Firstly you want a better way of saying what the default state is before the user has made any explicit decisions. 首先,您需要一种更好的方式来说明在用户做出任何明确决定之前的默认状态。 Next you want to know when you should refresh your in-app state from user preferences. 接下来,您想知道何时应该从用户首选项刷新应用内状态。

Defaults Initialization 默认初始化

The first item's solution is to put the default values into the registration domain for your user preferences. 第一项的解决方案是将默认值放入用户首选项的注册域中 This is something you'll do during application initialization, rather than having your views individually check preferences and update them at initialization time. 这是您在应用程序初始化期间要做的事情,而不是让您的视图单独检查首选项并在初始化时更新它们。 The preferences system looks in quite a few places for data, and the first place it looks is in the in-memory-only registration domain. 首选项系统在很多地方查找数据,它看起来的第一个位置是仅在内存中的注册域。 This is where you'll put the default (ie no value specified means 'x') values for each of your user preferences. 这是您为每个用户首​​选项设置默认值(即没有指定值表示'x')的值。

The API you'll use for this is -[NSUserDefaults registerDefaults:] , which takes an NSDictionary of values. 您将使用的API是-[NSUserDefaults registerDefaults:] ,它采用值的NSDictionary To set your default values of YES (in Objective-C the BOOL type uses YES and NO rather than TRUE and FALSE ) you'll use something like this, commonly executed in a +initialize method for your application's main class: 要设置默认值YES (在Objective-C中,BOOL类型使用YESNO而不是TRUEFALSE ),您将使用类似的东西,通常在应用程序主类的+initialize方法中执行:

+ (void) initialize
{
    // in any +initialize, make sure it's being called on your class
    // +initialize is different from all other methods in this respect
    if ( [self isKindOfClass: [MyApplicationDelegate class]] == NO )
        return;    // being called on a superclass, don't do my stuff

    // set up default values for certain preferences
    NSMutableDictionary * defaults = [NSMutableDictionary new];
    [defaults setObject: [NSNumber numberWithBool: YES] forKey: @"additionKey"];
    [defaults setObject: [NSNumber numberWithBool: YES] forKey: @"subtractionKey"];
    [defaults setObject: [NSNumber numberWithBool: YES] forKey: @"multiplicationKey"];
    [defaults setObject: [NSNumber numberWithBool: YES] forKey: @"divisionKey"];

    // set this as the registration domain
    [[NSUserDefaults standardUserDefaults] registerDefaults: defaults];
    [defaults release];
}

Usually you'll put everything into one method like this, so if you have other parts of the application which expect a default non-zero value for any preference, you should add those to this group and place it into your application delegate's +initialize method. 通常你会把所有东西放到这样的一个方法中,所以如果你有其他部分的应用程序期望任何偏好的默认非零值,你应该将它们添加到这个组并将它放入你的应用程序委托的+initialize方法。

Now, when your other classes use [[NSUserDefault standardUserDefaults] boolForKey: @"additionKey"] and there is nothing saved to the preference files for that key, they will get the value supplied above. 现在,当你的其他类使用[[NSUserDefault standardUserDefaults] boolForKey: @"additionKey"]并且没有保存到该键的首选项文件时,它们将获得上面提供的值。

Refreshing Cached Values 刷新缓存值

So, you have a view where the user is able to change these preferences. 因此,您可以看到用户可以更改这些首选项的视图。 Your next job is to make the view above that update its member variables using the new preferences. 您的下一个工作是使上面的视图使用新的首选项更新其成员变量。 For this we can use either delegation or notification. 为此,我们可以使用委托或通知。 In this case, I'll go with notifications for to reasons: 在这种情况下,我将通知原因:

  1. You're using NSUserDefaults, which can theoretically change in many different places. 您正在使用NSUserDefaults,理论上可以在许多不同的地方进行更改。 Delegation would only inform you of changes made by one object. 委派只会告知您一个对象所做的更改。
  2. NSUserDefaults already implements a handy notification which you can watch. NSUserDefaults已经实现了一个方便的通知,您可以观看。

So, in your flashcardsViewController you'll have something like these few methods: 所以,在你的flashcardsViewController中,你将拥有类似这些方法的东西:

- (void) updateFromPreferences
{
    // fetch current values from user defaults into your member variables
    additionIsEnabled = [[NSUserDefaults standardUserDefaults] boolForKey: @"additionKey"];
    // etc...
}

- (void) viewWillLoad
{
    // load variables from user defaults
    [self updateFromPreferences];

    // find out when the preferences have been changed
    // this will cause -updateFromPreferences to be called
    // whenever something changes preferences, inside the app or outside
    [[NSNotificationCenter defaultNotificationCenter] addObserver: self
                                                         selector: @selector(updateFromPreferences)
                                                             name: NSUserDefaultsDidChangeNotification
                                                           object: nil];
}

- (void) viewDidUnload
{
    // always remove notification observers.
    [[NSNotificationCenter defaultNotificationCenter] removeObserver: self
                                                                name: NSUserDefaultsDidChangeNotification
                                                              object: nil];
}

- (void) dealloc
{
    // add this to your existing dealloc routine
    [[NSNotificationCenter defaultNotificationCenter] removeObserver: self
                                                                name: NSUserDefaultsDidChangeNotification
                                                              object: nil];
}

Summary 摘要

Taken together, these two should give you everything you need to make this work. 总而言之,这两个应该为您提供完成这项工作所需的一切。

In addition to the "always YES" problem pointed out by Chiefly Izzy , your -buttonClicked: method does not read new values from NSUserDefaults . 除了-buttonClicked: Izzy指出的“always YES”问题之外,你的-buttonClicked:方法不会从NSUserDefaults读取新值。 These values are read (once) in [flashcardsViewController viewDidLoad] . 这些值在[flashcardsViewController viewDidLoad]中读取(一次)。 If they are changed in settings, the change will not be detected in flashCardsViewController until the next time it is loaded (probably the next time the application is launched). 如果在设置中更改了它们,则在下次加载flashCardsViewController之前不会检测到更改(可能是下次启动应用程序时)。

The simplest approach would be to move your code for reading the IsEnabled BOOL values into the -buttonClicked method, like so: 最简单的方法是将用于读取IsEnabled BOOL值的代码移动到-buttonClicked方法中,如下所示:

-(void) buttonClicked:(id)sender {

    // the following should assign each BOOL variable based on the key
    NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
    additionIsEnabled = [defaults boolForKey:@"additionKey"];
    subtractionIsEnabled = [defaults boolForKey:@"subtractionKey"];
    multiplicationIsEnabled = [defaults boolForKey:@"multiplicationKey"];
    divisionIsEnabled = [defaults boolForKey:@"divisionKey"];

    int a = rand() %  4;
    if ( additionIsEnabled || subtractionIsEnabled ...

This code ... 这段代码......

//the following should assign the keys if they don't exist  
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"additionKey"]){
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"additionKey"];
}

... simply always set's additionKey to TRUE . ......只是始终设置的additionKeyTRUE If you would like to check if additionKey is set, do this ... 如果您想检查是否设置了additionKey ,请执行以下操作...

//the following should assign the keys if they don't exist  
if (![[NSUserDefaults standardUserDefaults] objectForKey:@"additionKey"]){
    [[NSUserDefaults standardUserDefaults] setBool:TRUE forKey:@"additionKey"];
}

... boolForKey: documentation: If a boolean value is associated with defaultName in the user defaults, that value is returned. ... boolForKey: documentation:如果布尔值与用户默认值中的defaultName相关联,则返回该值。 Otherwise, NO is returned. 否则,返回NO。

Translated to human language - if there's an value associated with additionKey , this value is returned. 翻译为人类语言 - 如果存在与additionKey关联的值,则返回此值。 If there's no associated value, NO/FALSE is returned. 如果没有关联值,则返回NO / FALSE。

So, your code does this - if value is not associated with additionKey or if it is set to NO, set it to YES. 因此,您的代码执行此操作 - 如果value与additionKey无关或者如果设置为NO,则将其设置为YES。 This leads to this - additionKey is always set to YES/TRUE. 这导致了这一点 - additionKey总是设置为YES / TRUE。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM