2016-03-24 5 views
6

Это может быть ужасная ошибка в iOS 9.3 (выпуск).KVO, сломанный в iOS 9.3

При добавлении одного наблюдателя в [NSUserDefaults standardUserDefaults] Я заметил, что ответный метод -observeValueForKeyPath:ofObject:change:context: вызывается несколько раз.

В простом примере ниже, каждый раз, когда UIButton нажимается один раз, watchValueForKeyPath срабатывает дважды. В более сложных примерах он срабатывает еще больше раз. Он присутствует только на iOS 9.3 (как на сим, так и на устройствах).

Возможно, это может привести к хаосу приложения. Кто-нибудь другой испытывает то же самое?

// ViewController.m (barebones, single view app) 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    NSLog(@"viewDidLoad"); 
    [[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:@"SomeKey" options:NSKeyValueObservingOptionNew context:NULL]; 
} 

- (IBAction)buttonPressed:(id)sender { 
    NSLog(@"buttonPressed"); 
    [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"SomeKey"]; 
} 

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { 
    NSLog(@"observeValueForKeyPath: %@", keyPath); 
} 
+0

ли значение ключа NSUserDefaults наблюдаемым? Я не вижу никаких доказательств. Вы делали то, что у вас не было никаких ордеров. Вы не можете жаловаться, если он перестает работать. – matt

+0

@matt Я этого не думал. Однако, изучая это, я нашел следующее в NSUserDefaults.h: «NSUserDefaults можно наблюдать с помощью проверки значения ключа для любого сохраненного в нем ключа.» – Matt

+0

@Matt, Я смотрю в том же «NSUserDefaults.h»? Я не могу найти комментарий, который вы разместили в этом заголовке. –

ответ

4

Да я испытываю это, как хорошо, и это, кажется, ошибка, ниже это быстрый обходной путь я использую на данный момент, пока это не будет устранена. Я надеюсь, что это помогает!

Также, чтобы уточнить, поскольку iOS 7 KVO отлично работает с NSUserDefaults, и это, безусловно, является ключевым значением, наблюдаемым, как утверждает Мэтт, он явно написан в NSUserDefaults.h в iOS 9.3 SDK: «Наблюдаются NSUserDefaults используя ключ-значение для любого Наблюдения ключа, хранящегося в нем «.

#include <mach/mach.h> 
#include <mach/mach_time.h> 

@property uint64_t newTime; 
@property uint64_t previousTime; 
@property NSString *previousKeyPath; 

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 
    //Workaround for possible bug in iOS 9.3 SDK that is causing observeValueForKeyPath to be called multiple times. 
    newTime = mach_absolute_time(); 
    NSLog(@"newTime:%llu", newTime); 
    NSLog(@"previousTime:%llu", previousTime); 

    //Try to avoid duplicate calls 
    if (newTime > (previousTime + 5000000.0) || ![keyPath isEqualToString:previousKeyPath]) { 
     if (newTime > (previousTime + 5000000.0)) { 
      NSLog(@"newTime > previousTime"); 
      previousTime = newTime; 
      NSLog(@"newTime:%llu", newTime); 
      NSLog(@"previousTime:%llu", previousTime); 
     } 
     if (![keyPath isEqualToString:previousKeyPath]) { 
      NSLog(@"new keyPath:%@", keyPath); 
      previousKeyPath = keyPath; 
      NSLog(@"previousKeyPath is now:%@", previousKeyPath); 
     } 
     //Proceed with handling changes 
     if ([keyPath isEqualToString:@“MyKey"]) { 
      //Do something 
     } 
    } 
} 
+1

Отличный ответ. Работает на меня! Обратите внимание, что ваш @ MyKey содержит странную цитату после @, что Xcode жалуется на – Omar

2

при добавлении одного наблюдателя [NSUserDefaults standardUserDefaults] я заметил, что реагирующий метод -observeValueForKeyPath:ofObject:change:context: вызывается несколько раз

Это известная проблема и является reported (от Apple), как исправлено в iOS 11 и macOS 10.13.

+0

привет, где doc? – frank

+0

@frank https://developer.apple.com/library/content/releasenotes/Foundation/RN-Foundation/ index.html «Это также должно исправить дублирующиеся уведомления о некоторых изменениях, которые доставляются наблюдателям» - именно то, о чем говорит OP. – matt

+0

Спасибо за ваш ответ. – frank

0

Добавление этого ответа для MacOS (10.13), который определенно имеет ошибку, получающую несколько уведомлений для KVO из NSUserDefault Keys, а также относится к отклонениям. Лучше использовать расчет для прошедших nano секунд, который получает его для машины, на которой вы работаете. Делают это так:

#include <mach/mach.h> 
#include <mach/mach_time.h> 
static mach_timebase_info_data_t _sTimebaseInfo; 

uint64_t _newTime, _previousTime, _elapsed, _elapsedNano, _threshold; 
NSString *_previousKeyPath; 

-(BOOL)timeThresholdForKeyPathExceeded:(NSString *)key thresholdValue:(uint64_t)threshold 
{ 
    _previousTime = _newTime; 
    _newTime = mach_absolute_time(); 

    if(_previousTime > 0) { 
     _elapsed = _newTime - _previousTime; 
     _elapsedNano = _elapsed * _sTimebaseInfo.numer/_sTimebaseInfo.denom; 
    } 

    if(_elapsedNano > threshold || ![key isEqualToString:_previousKeyPath]) { 
     if(![key isEqualToString:_previousKeyPath]) _previousKeyPath = key; 
      return YES; 
     } 
     return NO; 
    } 
} 

-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{ 
    if(![self timeThresholdForKeyPathExceeded:keyPath thresholdValue:5000000]) return; // Delete this line of MacOS bug ever fixed 
    } 
    // Else this is the KeyPath you are looking for Obi Wan, process it. 
} 

Это основано на листинге 2 этого Apple, Doc: https://developer.apple.com/library/content/qa/qa1398/_index.html

Смежные вопросы