Iphone NSUserDefaults正确使用和检测更改

Iphone NSUserDefaults正确使用和检测更改,iphone,objective-c,ios4,Iphone,Objective C,Ios4,我是一个初学者,正在构建一个iOS通用应用程序,它在应用程序生命周期的各个阶段使用NSUserDefaults,包括一个应用程序内页面来设置这些变量。我发现我的UserDefaults经常与UI不同步,编写代码检查这些首选项会变得单调乏味 正如你在下面看到的,我正在检查“使用相机”的偏好是否设置得非常好;一定有更简单的方法 加载允许应用程序内编辑用户首选项的viewController时,情况会变得更糟: 是否有某种方法可以消除一些代码量(从而最大限度地减少内存优化的调试/工作),而不必在很多时

我是一个初学者,正在构建一个iOS通用应用程序,它在应用程序生命周期的各个阶段使用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
    }
}
我的主视图控制器:

#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
    }   
}
- (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];
}
设置可视控制器:

#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
    }   
}
- (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];
}

请看一下本主题,其中介绍了如何注册默认更改的更改通知


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

一种方法是去掉应用程序委托中的字段,只使用NSUserDefaults。我很抱歉不确定这个开销,但是有一个带有静态访问器的“CONFIG”类,也可以考虑使用BoL(和其他)访问器,如:

@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"];
    }
然后从任何地方访问它们都很简单。我将在同一个配置类中使用NSBundle和NSProcessInfo,然后我的应用程序将所有配置都放在一个地方

另外,根据我所读到的,您通常不必调用synchronize