IOS NSUserDefaults Наблюдение за изменением значений для одного ключа

У меня есть следующий код, целью которого является захват события изменения NSUserDefaults для определенного ключа.

 [[NSUserDefaults standardUserDefaults] addObserver:self
                                    forKeyPath:SOME_NSSTRING_VARIABLE
                                       options:NSKeyValueObservingOptionNew
                                       context:NULL];

 - (void)observeValueForKeyPath:(NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context
{NSLog (@"Changed for key %@", keyPath); }

Но watchValueForKeyPath никогда не будет вызван. Я даже попытался заменить SOME_NSSTRING_VARIABLE на строку, указанную в Замечание об изменении значения ключа NSUserDefaults, но это не помогло.

Обновление: Я изменяю NSUserDefaults из tabview. Вышеприведенный код для мониторинга изменений находится на другой вкладке одного и того же элемента управления tabview. На вкладке, где я отслеживаю изменения (вкладка, где существует код выше), если я добавлю:

- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//   NSLog(@"viewillappear");
NSUserDefaults *FUDefaults = [NSUserDefaults standardUserDefaults];
NSLog(@"The username obtained is: %@", [FUDefaults objectForKey:SOME_NSSTRING_VARIABLE]);
}

обновленное значение NSUserDefaults получается правильно, но watchValueForKeyPath никогда не вызывался.

Ответ 1

Изменить: viewDidUnload теперь устарел, используйте dealloc вместо removeObserver

Это должно работать отлично, я только что протестировал здесь.

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[NSUserDefaults standardUserDefaults] addObserver:self
                                            forKeyPath:@"SomeKey"
                                               options:NSKeyValueObservingOptionNew
                                               context:NULL];
    // Testing...
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 
    [defaults setObject:@"test" forKey:@"SomeKey"];
    [defaults synchronize];
}

- (void)viewDidUnload
{
    [super viewDidUnload];

    [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:@"SomeKey"];
}

- (void)observeValueForKeyPath:(NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context
{
    if([keyPath isEqual:@"SomeKey"])
    {
       NSLog(@"SomeKey change: %@", change);
    }
}

Вещи, которые вы могли бы проверить.

  • Поместите точку останова в viewDidUnload и убедитесь, что вид не исчезает на вас (поскольку вы меняете SomeKey на другой viewController). Если это так, то, возможно, переместите свой регистр/код де-региста в init/dealloc в зависимости от того, как работает ваш VC.

  • Использовать явный KeyPath как @"SomeKey" не подстановку вроде SOME_NSSTRING_VARIABLE

Ответ 2

Версия Swift 3:

override func viewDidLoad() {
    super.viewDidLoad()
    UserDefaults.standard.addObserver(self, forKeyPath: "keyPath", options: .new, context: nil)
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "keyPath" {
        //Do something
    }
}

deinit {
    UserDefaults.standard.removeObserver(self, forKeyPath: "keyPath")
}

Ответ 3

Версия Swift:

func setUserDefaultsListener(){
    NSUserDefaults.standardUserDefaults().addObserver(self, forKeyPath: "keyPath", options: NSKeyValueObservingOptions.New, context: nil)
}

override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
    if keyPath == "keyPath" {
        //Do something
    }
}

deinit {
    NSUserDefaults.standardUserDefaults().removeObserver(self, forKeyPath: "keyPath")
}

Ответ 4

Версия Swift 5:

extension UserDefaults {
    @objc dynamic var keyPath: Int {
        return integer(forKey: "keyPath")
    }
}

СОВЕТ: убедитесь, что var точно так же, как keyPath

и где вы используете наблюдателя сделать:

var observer: NSKeyValueObservation?

override func viewDidLoad() {
    super.viewDidLoad()
    observer = UserDefaults.standard.observe(\.keyPath, options: [.initial, .new], changeHandler: { (defaults, change) in
        //Do something
    })
}

deinit {
    observer?.invalidate()
}