NSMenuItem的状态绑定到NSUserDefaults中的布尔值不保持同步

时间:2011-02-25 05:24:32

标签: nsuserdefaults cocoa-bindings nsmenuitem

我的主菜单(MainMenu.xib)中有一个名为“Word Wrap”的NSMenuItem。它的值绑定到我的共享用户默认控制器,也在XIB中实例化。它在选择时也会发送以下操作:

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

在我的app delegate的+initialize方法中,我使用默认值填充标准用户默认值:

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

我的问题是我的NSMenuItem的状态与我的用户默认值不同步。以下是发生的事情的时间表:

应用启动:

  • 未选中Word Wrap菜单项
  • wordWrapIsEnabled没有
  • 自动换行

第一次选择Word Wrap:

  • 已选中“自动换行”菜单项
  • wordWrapIsEnabled不是(BZZZT WRONG)
  • 自动换行是(BZZZT WRONG)

第二次选择Word Wrap:

  • 未选中Word Wrap菜单项
  • wordWrapIsEnabled是YES (BZZZT WRONG)
  • Word Wrap已开启(BZZZT WRONG)

无限重复触发器。

我已经检查过以确保我的项目中没有任何内容可以访问wordWrapIsEnabled。通过绑定调用选择器和设置wordWrapIsEnabled之间是否存在竞争条件?我一直在假设首先设置绑定值。

2 个答案:

答案 0 :(得分:10)

当您单击具有绑定state(或value)属性的菜单项时,菜单项都会触发其操作翻转绑定值。并且似乎无法保证这两个操作的顺序,请参阅以下thread on Cocoa Builder

  

谢谢,我不是很确定   因为我做了几次改变   项目,但我认为这可以   被认为是10.5 sdk的错误,因为   我开始时就开始发生了   为它编译。 (几乎)相同   以虎为目标的项目   总是改变绑定值   执行目标操作之前 ,无论它是否为   按钮或menuItem。显然这个   一致性已被打破   豹。之后我可能会发布错误报告   一些测试来确认它。

还有一个相关的Radar bug report说菜单项不应自动翻转绑定值。对于你的问题,这可能为时已晚,但希望下次有人遇到这个问题时会有所帮助。

答案 1 :(得分:2)

当你使用Cocoa绑定到NSMenuItem的共享用户默认值时,你应该停止使用NSMenuItem的选择器,而是使用键值观察来确定值何时发生了变化,然后采取适当的行动。

在这个例子中,我有一个NSMenuItem绑定的useTransparency值名称。在我的控制器的init中,我注册接收此值的更新:

    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的选择器 - 我只是让键值观察完成这项工作。如果绑定到选择器,则会遇到尝试猜测值何时将更改而不是选择器被触发的问题。通过使用绑定系统而不是两者的混合来完全避免整个问题。