NSUserDefaults正确使用和检测更改

时间:2011-04-14 01:03:26

标签: iphone objective-c ios4

我是一个初学者,并构建一个iOS Universal应用程序,它在应用程序生命周期的各个阶段使用NSUserDefaults - 包括一个用于设置这些变量的应用程序内页面。我发现我的UserDefaults经常与UI不同步,编写代码来检查这些首选项会很繁琐。

如下所示,我正在检查“使用相机”首选项是否设置了很多;必须有一个更简单的方法......?

当我们加载允许应用内编辑用户首选项的viewController时,情况会更糟:

是否有某种方法可以消除一些代码量(因此,最大限度地减少内存优化的调试/工作量),而不是必须在很多时候检查首选项的值?我是否应该使用NSUserDefaultsDidChangeNotification检测到更改,并在此通知方法中设置一次AppDelegate变量? 示例(显而易见的遗漏假定那些方法存在......与我的偏好无关:

在我的AppDelegate中:

#progma mark Application Lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];

    // check values; if not present, set some defaults.
    if (![prefs stringForKey:@"pfUseCamera"]) {
        [prefs setValue:@"NO" forKey:@"pfUseCamera"];
    }

    if (![prefs stringForKey:@"pfWorkInBackground"]) {
        [prefs setValue:@"NO" forKey:@"pfWorkInBackground"];
    }

    [prefs synchronize];

    self.backgroundTasksTimer = [NSString stringWithString:[prefs stringForKey:@"pfWorkInBackground"]];
    self.shouldUseCamera = [NSString stringWithString:[prefs stringForKey:@"pfUseCamera"]];

    // start our timer
    // chose to run this timer ALL-the-time, dispite the user-preference; instead, see below that the timer method returns based on the user preference.
    [NSTimer scheduledTimerWithTimeInterval:10.0 target:self selector:@selector(backgroundTasks:) userInfo:nil repeats:YES];
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
    // pretty much the same as above
    // except NSTimer, which is controlled elsewhere
}

#progma mark AppDelegate-controlled Methods
- (void)backgroundTasks:(NSTimer *)timer { 
    if (![self.backgroundTasksTimer boolValue]) {
        return;
    } else {
        // do stuff in a timer begun in AppDelegate only if this preference is NOT NO
    }
}

我的主要ViewController:

#progma mark ViewController Lifecycle
- (void)viewDidLoad {
    [super viewDidLoad];
    MyAppDelegate *app = [MyAppDelegate sharedAppDelegate]; // I have this set up in the AppDelegate to allow this
    if ([app.shouldUseCamera boolValue]) {
        // init the camera and have it ready for use
    }
}

- (void)viewDidAppear:(BOOL)animated {
    MyAppDelegate *app = [MyAppDelegate sharedAppDelegate]; // I have this set up in the AppDelegate to allow this
    if ([app.shouldUseCamera boolValue]) {
        // resume the camera capture
    }
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    MyAppDelegate *app = [MyAppDelegate sharedAppDelegate]; 
    if ([app.shouldUseCamera boolValue]) {
        // suspend the camera capture
    }
}

- (void)viewDidUnload {
    MyAppDelegate *app = [MyAppDelegate sharedAppDelegate]; 
    if ([app.shouldUseCamera boolValue]) {
        // unload the camera
    }
}

#progma mark User/IBAction Methods
-(IBAction)doStuff:(id)sender {
    MyAppDelegate *app = [MyAppDelegate *app = [MyAppDelegate sharedAppDelegate]; sharedAppDelegate];   

    // take a picture
    if ([app.shouldUseCamera boolValue]) {
        [NSThread detachNewThreadSelector:@selector(takePicture:) toTarget:self withObject:fileName];  // take a photo and do stuff to it in a separate thread
    }   
}

SettingsViewController:

- (void)viewDidLoad {
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    MyAppDelegate *app = [MyAppDelegate sharedAppDelegate];

    // set UITextField to value of String Preference
    someUITextField.text = [prefs stringForKey:@"pfSomeOtherValue"];

    // set UISwitch to value of PSToggleSwitchSpecifier
    if ([app.backgroundTasksTimer boolValue]) {
        [shouldExecuteBackgroundTasks setOn:YES];
    } else {
        [shouldExecuteBackgroundTasks setOn:NO];
    }

    // set UISwitch to value of PSToggleSwitchSpecifier
    if ([app.shouldUseCamera boolValue]) {
        [shouldUseCamera setOn:YES];
    } else {
        [shouldUseCamera setOn:NO];
    }

- (IBAction)done:(id)sender {
    NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
    MyAppDelegate *app = [MyAppDelegate sharedAppDelegate];
    [prefs setObject:companyID.text forKey:@"pfCompanyID"];

    if ([shouldUseCamera isOn]) {
        [prefs setObject:@"YES" forKey:@"pfUseCamera"];
        app.backgroundTasksTimer = @"YES";
    } else {
        [prefs setObject:@"NO" forKey:@"pfUseCamera"];
        app.backgroundTasksTimer = @"NO";
    }

    if ([shouldExecuteBackgroundTasks isOn]) {
        [prefs setObject:@"YES" forKey:@"pfWorkInBackground"];
        app.shouldUseCamera = @"YES";
    } else {
        [prefs setObject:@"NO" forKey:@"pfWorkInBackground"];
        app.shouldUseCamera = @"NO";
    }
    [prefs synchronize];

    [self.delegate settingsControllerDidFinish:self];
}

2 个答案:

答案 0 :(得分:2)

一种方法是删除应用委托中的字段,然后使用NSUserDefaults。我不确定这有什么开销但是有一个带有静态访问器的“Config”类。此外,您可以考虑使用bool(和其他)访问器,如:

@implementation Config

    +(BOOL)shouldUseCamera {
       NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
       return [defaults boolForKey:@"shouldUseCamera"];
    }

    +(void)setShouldUseCamera:(BOOL)should {
       NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
       [defaults setBool:should forKey:@"shouldUseCamera"];
    }

然后从任何地方访问这些都是蛋糕。我将在同一个Config类中使用NSBundle和NSProcessInfo,然后我的应用程序只有一个地方可用于所有配置。

另外,根据我的阅读,您通常不必调用同步。

答案 1 :(得分:0)

查看此主题,该主题通过注册默认更改的更改通知。 Nsuserdefaultschange

如果多个视图需要检查此值,您可以将值公开为应用委托的属性,并在需要时检查该属性。应用程序委托将订阅通知并在触发时重新加载属性值。