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

发布于 2024-10-18 22:24:55 字数 1567 浏览 10 评论 0原文

我的主菜单(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 为 YES (BZZZT 错误)
  • 自动换行已打开 (BZZZT 错误)

重复反复无常。

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

I have an NSMenuItem titled "Word Wrap" in my main menu (MainMenu.xib). Its value is bound to my shared user defaults controller, also instantiated in the XIB. It also sends the following action when selected:

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

In my app delegate's +initialize method, I populate the standard user defaults with default values:

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

My problem is that my NSMenuItem's state is not staying in sync with my user defaults. Here is a timeline of what happens:

App launch:

  • Word Wrap menu item not checked
  • wordWrapIsEnabled is NO
  • Word wrap is OFF

1st time Word Wrap is selected:

  • Word Wrap menu item checked
  • wordWrapIsEnabled is NO (BZZZT WRONG)
  • Word wrap is OFF (BZZZT WRONG)

2nd time Word Wrap is selected:

  • Word Wrap menu item not checked
  • wordWrapIsEnabled is YES (BZZZT WRONG)
  • Word Wrap is ON (BZZZT WRONG)

Repeat flip-flop ad infinitum.

I've checked to make sure there is nothing else in my project that accesses wordWrapIsEnabled. Could there be a race condition between the invocation of the selector and the setting of wordWrapIsEnabled via the binding? I've been assuming that the bound value gets set first.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

最近可好 2024-10-25 22:24:55

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

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

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

When you click a menu item with a bound state (or value) property, the menu item both triggers its action and flips the bound value. And the order of these two operations does not seem to be guaranteed, see the following thread on Cocoa Builder:

Thanks, I am not absolutely sure
because I did several changes in my
project but I think that this can be
considered a bug of 10.5 sdk, because
it started to happen when I started
to compile for it. The (almost) same
project when it was targeted for Tiger
always changed the bound value
before the target-action was executed, regardless if it was a
button or a menuItem. Apparently this
consistency has been broken in
Leopard. I may post a bug report after
some testing to confirm it.

There’s also a related Radar bug report saying that menu items should not flip the bound value automatically. This is probably too late as an answer to your question, but hopefully will help next time somebody runs into this issue.

孤者何惧 2024-10-25 22:24:55

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

When you're using Cocoa bindings to Shared User Defaults for a NSMenuItem, you should stop using the selector for the NSMenuItem and instead use key-value observing to determine when the value has changed and then act appropriately.

In this example, I've got a useTransparency value name that an NSMenuItem is bound to. In my controller's init, I register to receive updates to this value:

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

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

Then later I implement the observer method:

-(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];
    }
}

In particular, I do not bind a selector for the NSMenuItem at all - I just let key-value observing do the job. If you bind to the selector, you run into the problem of trying to guess when the value will change versus the selector being fired off. Avoid that whole problem completely by just using the bindings system rather than a mix of the two.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文