NSMenuItem 绑定到 NSUserDefaults 中布尔值的状态不保持同步
我的主菜单(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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
当您单击具有绑定
state
(或value
)属性的菜单项时,该菜单项会触发其操作并翻转绑定值。而且这两个操作的顺序似乎不能保证,参见下面的Cocoa Builder 上的线程:还有一个相关的 雷达错误报告 指出菜单项不应自动翻转绑定值。这对于您的问题的答案可能为时已晚,但希望下次有人遇到此问题时会有所帮助。
When you click a menu item with a bound
state
(orvalue
) 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: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.
当您使用 Cocoa 绑定到 NSMenuItem 的共享用户默认值时,您应该停止使用 NSMenuItem 的选择器,而是使用键值观察来确定值何时发生更改,然后采取适当的操作。
在此示例中,我有一个 NSMenuItem 绑定到的
useTransparency
值名称。在我的控制器的 init 中,我注册以接收对此值的更新:然后稍后我实现观察者方法:
特别是,我根本不绑定 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:Then later I implement the observer method:
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.