开发者

NSUserDefaults proper usage and Detecting Change

开发者 https://www.devze.com 2023-02-24 06:36 出处:网络
I\'m a bit of a beginner, and building an iOS Universal app which makes use of NSUserDefaults at various stages of the application lifecycle - including an in-App page to set these variables. I find t

I'm a bit of a beginner, and building an iOS Universal app which makes use of NSUserDefaults at various stages of the application lifecycle - including an in-App page to set these variables. I find that my UserDefaults are often out-of-sync with the UI, and it gets tedious to write code to check for these preferences.

As you can see below, I'm checking to see if the "Use Camera" preference is set an aweful lot; there must be an easier way...?

Get's worse when we load the viewController that allows in-app editing of user preferences:

Is there some way to eliminate some of the amount of code (and therefore, minimize debugging/effort for memory optimization), instead of having to check in to what the value of the preferences are a lot of the time? Should I be using the NSUserDefaultsDidChangeNotification to detected a change, and set AppDelegate variables once inside this notification method? Example (omissions of the obvious assumes those methods are there... just not related to my preferences:

In my 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 开发者_JAVA技巧a timer begun in AppDelegate only if this preference is NOT NO
    }
}

My primary 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];
}


one approach is to get rid of the fields in your app delegate and just use NSUserDefaults. I'm not sure of the overhead with this but have a "Config" class with static accessors. Also, you might consider using the bool (and other) accessors like:

@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"];
    }

Then it's cake to access these from anywhere. I'll use NSBundle and NSProcessInfo in the same Config class and then my app has a single place for all config.

Also, from what I've read, you don't normally have to call synchronize.


Take a look at this topic which goes through registering for the change notification on default changes. Nsuserdefaultschange

If multiple views need to check this value you could just expose the value as a property of your app delegate and check that property when needed. The app delegate would subscribe to the notification and reload the property value when it fires.

0

精彩评论

暂无评论...
验证码 换一张
取 消