2

我的主菜单(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
    }
}

在我的应用程序委托的+initialize方法中,我使用默认值填充标准用户默认值:

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

我的问题是我的 NSMenuItem 的状态与我的用户默认设置不同步。以下是发生的时间线:

应用启动:

  • 未选中自动换行菜单项
  • wordWrapIsEnabled没有
  • 自动换行已关闭

第一次选择自动换行:

  • 选中自动换行菜单项
  • wordWrapIsEnabled(BZZZT 错误)
  • 自动换行已关闭(BZZZT 错误)

第二次选择自动换行:

  • 未选中自动换行菜单项
  • wordWrapIsEnabled(BZZZT 错误)
  • 自动换行已打开(BZZZT 错误)

无限重复触发器。

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

4

2 回答 2

11

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

谢谢,我不太确定,因为我在项目中做了一些更改,但我认为这可以被认为是 10.5 sdk 的错误,因为它在我开始编译时就开始发生了。以 Tiger 为目标的(几乎)同一个项目总是 在执行目标操作之前更改绑定值,无论它是按钮还是菜单项。显然,这种一致性在 Leopard 中被打破了。我可能会在一些测试后发布错误报告以确认它。

还有一个相关的雷达错误报告说菜单项不应该自动翻转绑定值。作为对您问题的回答,这可能为时已晚,但希望下次有人遇到此问题时会有所帮助。

于 2011-04-08T07:23:41.837 回答
2

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

于 2012-11-28T20:06:13.827 回答