Состояние NSMenuItem, связанного с логическим значением в NSUserDefaults, не синхронизированным - PullRequest
1 голос
/ 25 февраля 2011

У меня есть NSMenuItem под названием «Word Wrap» в моем главном меню (MainMenu.xib). Его значение привязано к общему контроллеру пользователя по умолчанию, также созданному в XIB. Он также отправляет следующее действие при выборе:

- (IBAction)toggleWordWrap:(id)sender {
    NSUserDefaultsController *ctrlr = [NSUserDefaultsController sharedUserDefaultsController];
    if ([[[ctrlr values] valueForKey:@"wordWrapIsEnabled"] boolValue]) {
        // turn on word wrap
    } else {
        // turn off word wrap
    }
}

В методе +initialize участника моего приложения я заполняю стандартные пользовательские значения по умолчанию значениями по умолчанию:

+ (void)initializeDefaults {
    NSDictionary *defaults = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:NO], @"wordWrapIsEnabled",
                             // etc.
                             nil];
    NSUserDefaultsController *ctrlr = [NSUserDefaultsController sharedUserDefaultsController];
    [ctrlr setInitialValues:defaults];
}

Моя проблема в том, что состояние моего NSMenuItem не синхронизируется с настройками по умолчанию для моего пользователя. Вот график того, что происходит:

Запуск приложения:

  • Пункт меню «Перенос слов» не отмечен
  • wordWrapIsEnabled НЕТ
  • перенос слов выключен

Выбран первый раз перенос слов:

  • Пункт меню «Перенос слов» отмечен
  • wordWrapIsEnabled - это НЕТ (BZZZT WRONG)
  • Перенос слов выключен (BZZZT WRONG)

2-й раз Word Wrap выбран:

  • Пункт меню «Перенос слов» не отмечен
  • wordWrapIsEnabled - ДА (BZZZT НЕПРАВИЛЬНО)
  • Перенос слов включен (BZZZT WRONG)

Повторите триггер до бесконечности.

Я проверил, чтобы в моем проекте больше не было доступа к wordWrapIsEnabled. Может ли быть состояние гонки между вызовом селектора и настройкой wordWrapIsEnabled через привязку? Я предполагал, что значение привязки будет установлено первым.

Ответы [ 2 ]

10 голосов
/ 08 апреля 2011

Когда вы щелкаете элемент меню со связанным свойством state (или value), элемент меню одновременно запускает свое действие и , переворачивая связанное значение. И порядок этих двух операций, похоже, не гарантирован, см. Следующий поток на Cocoa Builder :

Спасибо, я не совсем уверен потому что я сделал несколько изменений в моем проект, но я думаю, что это может быть считается ошибка 10,5 SDK, потому что это начало происходить, когда я начал скомпилировать для этого. (Почти) то же самое проект, когда он был нацелен на тигра всегда менял связанное значение до целевое действие было выполнено независимо от того, было ли это кнопка или пункт меню. Видимо это последовательность была нарушена в Leopard. Я могу опубликовать сообщение об ошибке после некоторое тестирование, чтобы подтвердить это.

Есть также связанный Отчет об ошибке радара , в котором говорится, что пункты меню не должны автоматически переворачивать связанное значение. Возможно, это слишком поздно для ответа на ваш вопрос, но, надеюсь, поможет в следующий раз, когда кто-нибудь столкнется с этой проблемой.

2 голосов
/ 29 ноября 2012

Когда вы используете привязки Какао к общим пользовательским настройкам по умолчанию для NSMenuItem, вам следует прекратить использовать селектор для NSMenuItem и вместо этого использовать наблюдение значения ключа, чтобы определить, когда значение изменилось, и затем действовать соответствующим образом.

В этом примере у меня есть useTransparency имя значения, с которым связан NSMenuItem. В инициализации моего контроллера я регистрируюсь, чтобы получать обновления для этого значения:

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

    [userDefaults addObserver:self
                   forKeyPath:@"useTransparency"
                      options:NSKeyValueObservingOptionNew
                      context:NULL];

Затем я реализую метод наблюдателя:

-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void *)context
{
    NSLog(@"KVO: %@ changed property %@ to value %@", object, keyPath, change);

    if ([keyPath compare:@"useTransparency"] == NSOrderedSame)
    {
        BOOL isTransparent = [[change valueForKey:@"new"] boolValue];
        [self setTransparency:isTransparent];
    }
}

В частности, я вообще не связываю селектор для NSMenuItem - я просто позволяю наблюдению ключ-значение делать свою работу. Если вы привязываетесь к селектору, вы сталкиваетесь с проблемой попытки угадать, когда значение изменится в зависимости от срабатывания селектора. Избегайте всей этой проблемы, просто используя систему привязок, а не их комбинацию.

...